在循环完成nodejs请求之前执行的回调
callback executing before loop finishes nodejs request
我正在与 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
}
}]
for
循环无法像您希望的那样使用异步代码,因为它们仅用于同步代码。使用 async.map
来处理每个 ID 的异步请求。这将获取您的 ID 数组,对每个 ID 执行一个异步请求,并返回一组响应,然后您可以使用常规 Array.prototype.filter
对其进行过滤,然后最终将 matchedModules
传递给您的回调。
你遇到的问题是同步思考,异步工作。您在循环中触发您的请求,并在循环之后立即调用您的回调。但是您不能保证在调用回调方法时所有调用都已处理。实际上,相反的情况是非常有保证的。您需要的是一种仅在处理完所有请求后才调用回调的方法。
我认为你应该考虑使用 promises。 Promises 是 Ecmascript6 的一部分,如今通过各种库被广泛使用。最著名的(据我所知)是q,但还有其他的。
promise 模式尝试(并且做得很好)消除所谓的 'callback hell'。
这是一个模式示例:
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 名为 Promises 的文章——处理异步的另一种方法 JavaScript。
在您的特定情况下,一种解决方法是将承诺(为每个请求创建的每个承诺)收集到一个承诺数组中,然后使用 Q.all 方法。当所有承诺都已解决时,该承诺也已解决。
我正在与 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
}
}]
for
循环无法像您希望的那样使用异步代码,因为它们仅用于同步代码。使用 async.map
来处理每个 ID 的异步请求。这将获取您的 ID 数组,对每个 ID 执行一个异步请求,并返回一组响应,然后您可以使用常规 Array.prototype.filter
对其进行过滤,然后最终将 matchedModules
传递给您的回调。
你遇到的问题是同步思考,异步工作。您在循环中触发您的请求,并在循环之后立即调用您的回调。但是您不能保证在调用回调方法时所有调用都已处理。实际上,相反的情况是非常有保证的。您需要的是一种仅在处理完所有请求后才调用回调的方法。
我认为你应该考虑使用 promises。 Promises 是 Ecmascript6 的一部分,如今通过各种库被广泛使用。最著名的(据我所知)是q,但还有其他的。
promise 模式尝试(并且做得很好)消除所谓的 'callback hell'。
这是一个模式示例:
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 名为 Promises 的文章——处理异步的另一种方法 JavaScript。
在您的特定情况下,一种解决方法是将承诺(为每个请求创建的每个承诺)收集到一个承诺数组中,然后使用 Q.all 方法。当所有承诺都已解决时,该承诺也已解决。