如何解决 node.js 中可变数量的承诺

How to resolve a variable number of promises in node.js

我正在开发一个函数(由 express.js 路由调用)以将数据库中的事件信息与其 Facebook 对应项合并,并 return 将其作为事件对象数组。

我在处理 node.js 的异步特性时遇到问题,在 return 处理整个对象之前,我无法解决 foreach 循环中可变数量的承诺。我已经尝试了很多不同的方法来重新排列我的代码(回调、计数器、承诺等),但我没有成功解决这个问题,我真的很想知道为什么。我怀疑这与在 foreach 循环中被覆盖的变量有关,但我不确定如何解决。

我正在寻找三样东西:

  1. 我在概念上没有掌握解决这个问题所需的什么?
  2. 我将来如何解决或调试这个问题?
  3. 如何修复我的代码以使其正常工作?

这是我的函数:

function mergeEvents(req, res, next, events){

console.log("Merge Events");

var dfd = q.defer();

ensureAuthenticated(req, res, next).then(function(auth){
    var iEvent, event;
    var promises = [];

    if (auth){
        console.log("authenticated!");
        console.log("auth token: " + ACCESS_TOKEN);

        for (iEvent in events){
            event = events[iEvent];

            var promise = q.defer();
            promises.push(promise);

            https.get('https://graph.facebook.com/' + event.fb_id + '?access_token=' + ACCESS_TOKEN, function(response) {
                var str = '';
                response.on('data', function(chunk){
                    str += chunk;
                });

                response.on('end', function(){
                    var fb_event = JSON.parse(str);
                    event.dataValues.fb = fb_event;
                    promise.resolve(event);
                });
            });

            if (promises.length == events.length){
                console.log("last run through");
                q.all(promises).then(function(results){
                    console.log("all promises completed?");
                    console.log(results[0]); //OUTPUT BELOW
                    //more code in here... but promises haven't resolved
                    //...
                    dfd.resolve(events);
                });
            }
        }
    }else{
        console.log("Not authenticated. Redirecting to main page.");
        dfd.resolve(events);
    }
});

return dfd.promise;

}

虽然我正在尝试获取 JSON 对象,但它 return 是 console.log(results[0]) 上的未解决承诺:

{ promise: [object Object],
  resolve: [Function],
  fulfill: [Function],
  reject: [Function],
  notify: [Function] }

我查看过的代码参考:

哦,这是我的单个事件 fb/db 合并有效的函数,因此您可以比较:

function mergeEvent(req, res, next, event){
console.log("Merge Event");

var dfd = q.defer();

ensureAuthenticated(req, res, next).then(function(auth){
    if (auth){
        console.log("authenticated!");
        console.log("auth token: " + ACCESS_TOKEN);
        https.get('https://graph.facebook.com/' + event.fb_id + '?access_token=' + ACCESS_TOKEN, function(response) {
            var str = '';
            response.on('data', function(chunk){
                str += chunk;
            });

            response.on('end', function(){
                var fb_event = JSON.parse(str);
                event.dataValues.fb = fb_event;
                dfd.resolve(event);
            });
        });
    }else{
        console.log("not authenticated. redirecting to main page");
        dfd.resolve(event);
    }
});

return dfd.promise;
}

你的主要问题在这里:

var promise = q.defer();
promises.push(promise);

q.defer() 没有 return 承诺。它 return 是 deferred

var result = q.defer();
promises.push(result.promise);

正确命名变量很重要,您没有看到错误是因为您选择了不正确的变量名。


话虽这么说...

  • 避免for .. in。数组有 .forEach().map().
  • 不检查 if (promises.length == events.length),而是将该部分移出循环。
  • 您的函数很长,可以进行一些重构。
  • 当然,不要调用您的延迟对象 "deferred" 或您的承诺对象 "promise"。这不是描述性的。
  • 通读What is the explicit promise construction antipattern and how do I avoid it?(让它沉下去,需要一些时间)

这是我要使用的。

var q = require('q');
var qHttp = require("q-io/http"); // -> https://github.com/kriskowal/q-io

var FB = {
    // collect other FB API methods here, maybe transform into module
    graph: function (id) {
        var url = 'https://graph.facebook.com/' + id + '?access_token=' + ACCESS_TOKEN;
        return qHttp.read(url).then(function (data) {
            return JSON.parse(data.toString());
        });
    }
};

function mergeEvents(req, res, next, events) {
    return ensureAuthenticated(req, res, next).then(function (auth) {
        if (!auth) return q.reject("Not authenticated.");

        return q.all(events.map(function (event) {
            return FB.graph(event.fb_id).then(function (data) {
                event.dataValues.fb = data;
                return event;
            });
        }).then(function (results) {
            //more code in here...
        }));
    });
}

注意:如果您写了 ensureAuthenticated,请将其修改为直接拒绝,而不是使用每次使用时都需要检查的虚假 auth 值进行解析。之后可以删除行 if (!auth) ...

此外,处理 "enhanced" 事件的 //more code in here... 部分应该 可能 位于 mergeEvents 之外。