如何在Node.js中解决可变数量的Promise?

4
我正在编写一个函数(由express.js路由调用)来将数据库中的事件信息与其Facebook对应项合并,并将其作为事件对象数组返回。
我遇到了node.js异步性质和解决foreach循环内可变数量的promise之前返回整个对象的问题。我已经尝试过许多不同的代码重组方法(回调,计数器,promise等),但我还没有成功解决这个问题,我真的很想知道为什么。我怀疑这与在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对象时,控制台上会返回一个未解决的promise,如下所示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;
}
1个回答

6
你的主要问题在这里:
var promise = q.defer();
promises.push(promise);

q.defer() 不会返回一个 promise,而是返回一个 deferred(延迟对象)。

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

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


话虽如此...

  • 避免使用for .. in。数组有.forEach().map()
  • 如果要检查if (promises.length == events.length),请将该部分移出循环。
  • 你的函数相当长,需要进行一些重构。
  • 当然,不要将你的延迟对象称为“deferred”,也不要将你的promise对象称为“promise”,这样并不描述清楚。
  • 阅读什么是显式Promise构造反模式,如何避免它?(让它沉淀一下,需要一些时间)

以下是我会使用的内容。

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) ...这一行。此外,处理“增强”事件的//more code in here...部分可能应该放在mergeEvents之外。

谢谢!作为一个 promise 的初学者,这对我很有帮助,资源也很好 :) - smileham

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接