我正在与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
}
}]
答案 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方法。当所有承诺都得到解决时,这个承诺就会得到解决。