【问题标题】:Return value for parent function (nested); Waiting for asynchronous function父函数的返回值(嵌套);等待异步函数
【发布时间】:2014-11-21 22:09:45
【问题描述】:

我对异步函数没什么问题。 getImageSizeByUrl() 中的异步函数可以使用 $.Deferred() 来处理。

function getImageSizeByUrl(url) {
    var deferredReady = $.Deferred();

    var tmpImg = new Image();
    tmpImg.src = url;
    // asynchronous function
    $(tmpImg).on('load',function(){
        getImageSizeByUrlWidth = tmpImg.width;
        getImageSizeByUrlHeight = tmpImg.height;

        deferredReady.resolve();
    });

    return deferredReady.promise();
}

但是现在我还有另外两个问题。在较低的函数中,函数 $.when 本身是异步的。所以它只解决了问题。这样chrome.webRequest.onBeforeRequest.addListener()函数就完成了,无需等待getImageSizeByUrl()的返回。在这种棘手的情况下,如何获得同步行为?

我的第二个问题是低层函数中的嵌套返回值。 return {redirectUrl: "https://www.google.de/images/srpr/logo11w.png"};return {} 应该是父函数 function(info) 的返回值。

(背景:chrome扩展的lower listener函数会在加载前检查每张图片。listener需要一个返回值来重定向或允许图片加载。但是listener函数不会等待upper函数getImageSizeByUrl的结果并返回一个空值。)

// listener for image loading
chrome.webRequest.onBeforeRequest.addListener(
    function(info) {
        console.log("test1");
        $.when(
            getImageSizeByUrl(info.url)
        ).done( 
            function() {
                // for example: if image has to big height or width, load alternative image
                if(getImageSizeByUrlHeight > 200 || getImageSizeByUrlWidth > 200) {
                    // load alternative image
                    console.log("test3");
                    return {redirectUrl: "https://www.google.de/images/srpr/logo11w.png"};
                } else {
                    // image is not big. allow loading
                    console.log("test3");
                    return {};
                }
            }
        );
        console.log("test3");
    },
    {
        urls: [
            "<all_urls>",
        ],
        types: ["image"]
    },
    ["blocking"]
);

希望有人能够帮助我/给我一些提示。 :)
最好的问候

【问题讨论】:

  • 哇,现在我们每天有近 20 次询问如何从异步操作同步返回值。它根本无法完成,并且编写期望只是浪费代码的代码,需要对其进行重组以与异步操作一起使用。请阅读此stackoverflow.com/questions/14220321/… 以了解您的选择(提示您代码中的答案将在于正确使用 Promise 以将异步返回值传递给其他代码,并且由于 Promise 最终使用回调,这就是您将使用的.
  • 如果 onBeforeRequest 处理程序必须返回只能通过异步操作获得的值,那么您根本无法使代码以这种方式工作。您将需要一个不依赖于返回异步检索值的新设计。您要么需要提前获取这些值(因此它们已经可用),要么不使用 onBeforeRequest 并以其他方式解决您的整体问题。
  • 仅供参考,您根本不需要$.when()。你可以这样做 getImageSizeByUrl(info.url).done(...); $.when() 只有当你想在多个承诺都完成时得到通知时才需要。
  • 我还可能提到您的getImageSizeByUrl() 存在潜在问题。您需要在设置.src 属性之前设置load 事件处理程序。这是因为如果图像在浏览器缓存中,当您设置.src 属性时,某些浏览器(如某些版本的 IE)会立即触发load 事件。如果您的代码发生这种情况,您将错过load 事件。因此,只需将.src 的设置移至安装load 事件处理程序之后即可。
  • 感谢您的回答。我读了另一个几乎同样问题的问题。 stackoverflow.com/questions/9121902/… 好像你说的不改变代码布局是没有办法解决这个问题的。

标签: jquery asynchronous callback nested deferred


【解决方案1】:

您可能会发现编写一个返回“承诺的图像”的构造函数更有用,即带有返回承诺的自定义 .setSrc() 方法的 window.Image 实例。

这样的事情(未经测试)应该这样做:

function PromisifiedImg() {
    var img = new Image();
    img.setSrc = function(src, limits) {
        var dfrd = $.Deferred();
        img.onload = function() {
            if(limits && (img.width > limits.width || img.height > limits.height)) {
                var error = new Error('img dimension(s) exceed limits');
                error.number = 101; // 101 is arbitrary
                dfrd.reject(error);
            } else {
                dfrd.resolve(img);
            }
        };
        img.onerror = function(e) {
            e.number = e.number || 0;
            dfrd.reject(e);
        };
        img.src = src;
        return dfrd.promise();
    };
    return img;
}

现在您可以调用返回的 Image 的 .setSrc(...) 方法而不是 .src = '...'

您的 ....addListener() 处理程序可能看起来像这样:

chrome.webRequest.onBeforeRequest.addListener(
    function(info) {
        var img = new PromisifiedImg();
        img.setSrc(info.url, { width:200, height:200 }).then(null, function(e) {
            if(e.number === 101) { // detect the custom error condition
                console.log(e.message);
                return img.setSrc("https://www.google.de/images/srpr/logo11w.png");
            }
        });
    },
    { urls: [ "<all_urls>" ], types: ["image"] },
    ["blocking"]
);

这样做的一个好处是返回的图像是可重用的。对 .setSrc() 的第二次调用将在同一个 Image 实例上运行,但会返回一个新的 Promise。

注意:我没有尝试穿透chrome.webRequest.onBeforeRequest.addListener() 包装器传递给它的其他参数。随便看了一眼documentation,这看起来可能是错误的方法。因此,这个答案很可能只提供部分解决方案。

【讨论】:

  • 但是,这并不能解决 OP 需要从侦听器回调中返回值的问题。
  • @jFriend00,你确定吗?我们清楚地以不同的方式阅读了这个问题。 OP 的声明“监听器需要返回值”[来自 getImageSizeByUrl()] 与“需要从监听器回调返回值”不同。
  • 到目前为止,这就是整个对话。他们希望从同步侦听器回调函数中返回一些重定向信息,并且该重定向信息基于异步图像大小。
  • 好的,但我对从听众那里返回一些东西的概念感到困惑。回到哪里?
  • 大概返回到监听回调的调用者。回调返回一个值并不是闻所未闻的。例如,jQuery 允许您从侦听器返回 false 以停止事件传播。 Promise 回调接受返回值(其他 Promise 或值)。
猜你喜欢
  • 1970-01-01
  • 2020-12-23
  • 1970-01-01
  • 1970-01-01
  • 2011-03-04
  • 2018-01-03
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多