【问题标题】:then() for Q.all() not being called未调用 Q.all() 的 then()
【发布时间】:2017-06-30 21:00:59
【问题描述】:

在 Node.js 中,我正在等待几个请求完成,然后再进行如下函数调用:

function loadQuotes(symbols){
    var promises = [];
    for(var s in symbols){
        var deferred = Q.defer();
        var url = rootURL;
        request(url, function (error, response, body) {
            if (!error && response.statusCode == 200) {
                var info = JSON.parse(body);
                    deferred.resolve();
                    console.log("resolved");
            }
        });
        promises.push(deferred.promise);
    }
    return Q.all(promises);
}

我正在等待所有请求如下

loadQuotes(symbols.slice(0,3)).then(function(data){
    console.log("done");
    httpServer();
}).done();

所有请求都已发出,但我没有看到控制台打印“完成”。 promises 变量确实是一个promise 列表,它们都已“解决”,但then 函数没有被调用。有什么想法吗?

【问题讨论】:

  • for (var s in symbols)?您不会在任何地方使用s。我错过了什么吗?
  • 我在 url 中使用它,我把那部分省略了
  • 您也没有在请求回调中处理错误情况(最好通过调用deferred.reject(error)),因此如果发生错误,您的承诺将永远无法解决。
  • 所有延迟解析,打印出适当数量的“resolved”
  • @Abundance 你还在使用 ES5 吗?

标签: node.js q deferred


【解决方案1】:

您有一个范围界定问题。您的变量 deferred 是整个函数的本地变量,而不仅仅是 for 循环,因此您在回调中使用它之前在循环中覆盖该变量,因此您不会解析您创建的所有延迟对象。请记住,回调会在 for 循环完全运行之后的某个时间发生。

一个简单的解决方法是更改​​为使用let 而不是var。这将使您的 deferred 变量成为 for 循环范围的局部变量,而不是整个函数范围。

但是,我首选的解决方法是制作一个承诺版本的request(),它返回一个承诺并使用它。

function rp(url) {
    return new Promise(function(resolve, reject) {
        request(url, function(err, response, body) {
            if (err) return reject(err);
            if (response.statusCode !== 200) reject(new Error(response.statusCode));
            resolve(body);
        });
    });
}

然后,你可以使用它:

function loadQuotes(symbols){
    var promises = [];
    for(var s in symbols){
        var url = rootURL;
        promises.push(rp(url));
    }
    return Promise.all(promises);
}

或者,如果你还真的需要使用 Q 库,你也可以使用它来编写上面的代码。

仅供参考,有一个 request-promise 库返回一个可以替代 request 库的承诺。你也可以使用它。


注意,假设 symbols 是一个数组,并且您想坚持使用 ES5 解决方案,您也可以将您的 for 循环切换为使用 .forEach(),这将为每次调用循环,也解决了你的问题。

function loadQuotes(symbols){
    var promises = [];
    symbols.forEach(function(s) {
        var deferred = Q.defer();
        var url = rootURL;
        request(url, function (error, response, body) {
            if (!error && response.statusCode == 200) {
                var info = JSON.parse(body);
                deferred.resolve(info);
                console.log("resolved");
            }
        });
        promises.push(deferred.promise);
    });
    return Q.all(promises);
}

2020 年 1 月编辑 - request() 模块处于维护模式

仅供参考,request 模块及其衍生模块(如request-promise)现在处于维护模式,不会积极开发以添加新功能。您可以阅读更多关于推理的信息herethis table 中有一个备选方案列表,其中对每个备选方案进行了一些讨论。我自己一直在使用got(),它从一开始就是使用 Promise 构建的,而且使用简单。

【讨论】:

  • 哎呀,忘了说forEach()...解决这个问题最实用的ES5模式。
【解决方案2】:

var 不使用词法作用域

function loadQuotes(symbols){
    var promises = [];
    for(var s in symbols){
        // here you declare `deferred` using `var`
        var deferred = Q.defer();
        var url = rootURL;
        request(url, function (error, response, body) {
            if (!error && response.statusCode == 200) {
                var info = JSON.parse(body);
                // this is called *later*
                // when it is overwritten
                // with the last value
                // for every iteration
                deferred.resolve();
                console.log("resolved");
            }
        });
        // here, you expect it to be resolved later
        promises.push(deferred.promise);
    }
    return Q.all(promises);
}

因此,基本上,您有一系列待处理的 Promise 正在等待,其中只有最后一个被解决,因为其余的 Promise 都丢失了。

解决方案

  • ES6 let 救援
function loadQuotes(symbols){
    var promises = [];
    for(var s in symbols){
        let deferred = Q.defer();
        var url = rootURL;
        request(url, function (error, response, body) {
            if (!error && response.statusCode == 200) {
                var info = JSON.parse(body);
                deferred.resolve();
                console.log("resolved");
            }
        });
        promises.push(deferred.promise);
    }
    return Q.all(promises);
}
  • .bind()你的匿名回调函数
function loadQuotes(symbols){
    var promises = [];
    for(var s in symbols){
        var deferred = Q.defer();
        var url = rootURL;
        request(url, function (deferred, error, response, body) {
            if (!error && response.statusCode == 200) {
                var info = JSON.parse(body);
                deferred.resolve();
                console.log("resolved");
            }
        }.bind(null, deferred));
        promises.push(deferred.promise);
    }
    return Q.all(promises);
}

附录

As was pointed out by @Karl-Johan Sjögren,当出现错误时,您需要在回调中deferred.reject(error),否则Q.all() 将永远无法解决,并且您的应用程序将在出现问题时挂起,而不是优雅地响应。

需要明确的是,我并不是说这导致了这里的问题,而是如果你不这样做就会产生问题。所以总结一下:

function loadQuotes(symbols){
    var promises = [];
    for(var s in symbols){
        var deferred = Q.defer();
        var url = rootURL;
        request(url, function (deferred, error, response, body) {
            if (!error && response.statusCode == 200) {
                var info = JSON.parse(body);
                // pass something meaningful here
                deferred.resolve(info);
                console.log("resolved");
            } else {
                // always reject errors when they occur
                deferred.reject(error);
            }
        }.bind(null, deferred));
        promises.push(deferred.promise);
    }
    return Q.all(promises);
}

【讨论】:

    猜你喜欢
    • 2017-11-03
    • 1970-01-01
    • 2021-10-14
    • 2015-01-25
    • 2018-03-13
    • 2014-01-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多