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

Posted

技术标签:

【中文标题】如何在 node.js 中解决可变数量的承诺【英文标题】:How to resolve a variable number of promises in node.js 【发布时间】:2015-07-01 01:02:54 【问题描述】:

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

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

我正在寻找三样东西:

    我在概念上没有掌握解决此问题所需的哪些概念? 以后我该如何解决或调试这个问题? 如何修复我的代码以使其正常工作?

这是我的功能:

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 对象时,它在 console.log(results[0]) 上返回一个未解决的承诺:

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

我看过的代码参考:

https://github.com/kriskowal/q https://github.com/kriskowal/q/wiki/API-Reference https://strongloop.com/strongblog/how-to-compose-node-js-promises-with-q/ http://thejsguy.com/javascript/node.js/2014/06/27/JavaScript-Flow-Control.html

哦,这是我的单个事件 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】:

你的主要问题在这里:

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

q.defer() 不返回承诺。它返回一个deferred

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

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


话说回来……

避免for .. in。数组有.forEach().map()。 不要检查if (promises.length == events.length),而是将该部分移出循环。 您的函数很长,可以进行一些重构。 当然,不要将您的延迟对象称为“延迟”或您的承诺对象称为“承诺”。这不是描述性的。 通读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) ...

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

【讨论】:

谢谢!作为一个有承诺的初学者,这非常有帮助,资源也是如此:)

以上是关于如何在 node.js 中解决可变数量的承诺的主要内容,如果未能解决你的问题,请参考以下文章

Node.js / Javascript 多重递归承诺在视图中返回

node.js 中的嵌套承诺是不是正常?

如何在 Node js 中承诺一个 mysql 池连接?

Node.js承诺中的readline

如何在 Node.js expressjs 的异步对象方法中处理未处理的承诺拒绝?

如何遍历 node.js 中的一系列承诺?