Q.all() 的 then() 未被调用

then() for Q.all() not being called

在 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();

已发出所有请求,但我没有看到 "done" 打印到控制台。 promises变量确实是一个promises列表,都已经"resolved"了,但是then函数没有被调用。有什么想法吗?

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

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

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

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 库 returns 是 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 are now in maintenance mode and will not be actively developed to add new features. You can read more about the reasoning here. There is a list of alternatives in this table with some discussion of each one. I have been using got() 我自己,它是从一开始就使用 promises 构建的,并且易于使用。

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);
}

所以基本上,您有一系列正在等待的未决承诺,其中只有最后一个被解决,因为其余的都在范围内丢失。

解决方案

  • 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);
}

附录

,你需要在你的回调中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);
}