在循环完成nodejs请求之前执行的回调

时间:2015-01-28 05:00:37

标签: javascript arrays node.js request lodash

我正在与API通信以获取有关一系列ID的信息。因此,Array中的每个id都需要对api的请求,如果它们与逻辑匹配,我想从响应中构建一个数据数组。但是,正在处理请求的函数的回调正在构建新数组之前执行。在处理对api的大量调用时,我倾向于经常遇到这个问题。我该如何解决这个具体的例子,将来解决这个问题的最佳方法是什么?

var request = require('request');
var _ = require('lodash');

var siteLayouts = [1550, 1552, 1554, 1556, 1558, 1560, 1562, 1564, 1566, 1568, 1570, 1572, 1574, 1730, 1734, 1736, 1738, 1740, 1896, 1898, 1900, 1902, 1904, 1906, 1908, 1910, 1914, 1922, 1924, 1926, 1928, 1930, 1932, 1934, 1936, 1938, 1940, 1942, 1944, 1946, 1948, 1950, 1952, 1954, 1956, 1958, 1960, 1962, 1964, 1966, 1968, 1970, 1972, 1974, 1976, 1978, 1980, 1984, 1986, 1988, 1990, 1992, 1994, 1996, 1998, 2000, 2002, 2004, 2006, 2008, 2010, 2012, 2014, 2016, 2020, 2022, 2030, 2032, 2034, 2036, 2038, 2040, 2042, 2044, 2046, 2048, 2052, 2054, 2056, 2060, 2062, 2064, 2066, 2068, 2070, 2072, 2122, 2124, 2148, 2154, 2156, 2270, 2272, 2274, 2374, 2418, 2688, 2692, 2968, 3756, 4094, 5122, 5524, 7326, 7494, 8704, 8886, 9226, 9232, 9234, 9236, 9238, 9830, 9836, 10052, 10054, 10056, 10999, 11083, 11085, 11429, 11513, 17279, 20397, 22285, 22287, 22289, 22291, 22293, 22295, 22807, 22809, 22811, 22813, 22815];

function getLayoutModules(siteLayouts, callback) {
    var matchedModules = [];
    for (var i = 0; i < siteLayouts.length; i++) {
        request('http://PRIVATE-API-URL/layout/' + siteLayouts[i], function(err, res, body) {
            if (!err && res.statusCode == 200) {
                var layoutModules = JSON.parse(body);
                var match = _.filter(layoutModules, {
                    'dtoLayoutModule': {
                        'ModuleName': 'Featured Content'
                    }
                });
                if (match.length > 0 && match[0].dtoLayoutModule) {
                    //console.log(match[0].dtoLayoutModule);
                    matchedModules.push(match[0].dtoLayoutModule);
                    console.log(matchedModules.length)
                }
            }
        });
    }
    callback(matchedModules);
}

getLayoutModules(siteLayouts, function(matchedModules) {
    console.log(matchedModules);
});

我已经验证了数据是通过console.log长度添加到最终数组的,但我首先看到回调console.log的长度。此处是使用_.filter

过滤后请求中的示例响应正文
[{
    RequestStatus: {
        StatusCode: '200',
        StatusTxt: 'Successful request.',
        Result: 'Successful request.',
        ValidationErrors: null
    },
    dtoLayoutModule: {
        Id: 116013,
        LayoutId: 10999,
        LayoutName: 'layout name',
        ModuleId: 7,
        ModuleName: 'Featured Content',
        DisplayName: 'name to display',
        Position: 4,
        Config: '<config><item name="layout" value="primary" /></config>',
        MaxContentCount: 4,
        CanInherit: false,
        IsInheritable: false,
        IsStaticModule: false
    }
}]

2 个答案:

答案 0 :(得分:1)

for循环不会像您希望的那样使用异步代码,因为它们仅用于同步代码。使用async.map处理每个ID的异步请求。这将获取您的ID数组,为每个ID执行异步请求,并返回一组响应,然后您可以使用常规Array.prototype.filter进行过滤,然后再将matchedModules传递给您的回调。

答案 1 :(得分:1)

你正在解决的问题是你正在同步思考,并且正在异步工作。您正在循环中触发请求,并在循环后立即调用您的回调。但是,您无法保证在调用回调方法时所有调用都已处理完毕。实际上相反的是相当保证。您需要的是仅在处理完所有请求后调用回调的方法。

我认为你应该考虑使用promises。 Promise是Ecmascript6的一部分,今天通过各种库强烈使用。最着名的一个(据我所知)是q,但还有其他的。

承诺模式尝试(并做得非常好)消除所谓的回调地狱&#39;。

这是模式的一个例子:

asyncCall()
  .then(function(data1){
     // do something...
     return anotherAsyncCall();
   })
   .then(function(data2){
     // do something...  
     return oneMoreAsyncCall();    
   })
   .then(function(data3){
      // the third and final async response
   })
   .fail(function(err) {
      // handle any error resulting from any of the above calls    
   })
   .done();

此示例取自this文章Promise - 一种处理异步JavaScript的替代方法。

在您的特定情况下,一种方法是将Promises(为每个请求创建每个promise)收集到promise数组中,然后使用Q.all方法。当所有承诺都得到解决时,这个承诺就会得到解决。