然后()为Q.all()没有被调用

时间:2017-06-30 21:01:00

标签: node.js q deferred

在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变量确实是一个承诺列表,它们都被解析了#34;但是then函数没有被调用。有什么想法吗?

2 个答案:

答案 0 :(得分:3)

你有一个范围问题。您的变量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库的promise。你也可以使用它。

注意,假设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);
}

答案 1 :(得分:1)

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

附录

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