我知道JavaScript中的闭包和回调,但显而易见的是我没有在直观的层面上得到它们。
我有一个小应用程序正在从API中抓取数据,我可以轻松地console.log
来自每个请求的响应,我的问题是我正在尝试收集数据并构建一个对象,以便在所有请求完成时保存到文件中。
我得到nodejs是一个单独的执行线程并且它不会阻塞但我无法弄清楚当所有内部请求完成时将回调放在哪里我可以console.log
构建的对象。您将看到我的console.log
行位于错误的位置并在内部请求的第一个响应之前执行。
分解
码
const limit = require("simple-rate-limiter");
let request = limit(require("request")).to(1).per(200);
let options = {
method: 'POST',
url: 'https://myendpoint/details',
headers: {
'cache-control': 'no-cache',
'Content-Type': 'application/json'
},
body: {
"token": "TOKEN",
"method": "countries"
},
json: true
};
global.package = {};
global.services = {};
let countryServices = [];
/*
Country fetch
*/
request(options, function (err, response, countryResponse) {
if (err) {}
package.countries = countryResponse;
countryResponse.forEach(function (entry) {
let innerOptions = {
method: 'POST',
url: 'https://myendpoint/details',
headers: {
'cache-control': 'no-cache',
'Content-Type': 'application/json'
},
body: {
"token": "TOKEN",
"method": "services"
},
json: true
};
//THIS LINE OMG
//let countryServices = [];
innerOptions.body.countryCode = entry.countryCode;
request(innerOptions, function (err, response, innerResponse) {
if (err) {}
countryServices.push(innerResponse);
console.log(" inner response " + entry.countryCode + ' : ' + JSON.stringify(innerResponse, null, ""));
});//END innerResponse
});//END countryResponse.forEach
services = countryServices;
console.log(JSON.stringify(package, null, ""));
console.log(JSON.stringify(countryServices, null, ""));
});//END orderResponse
countryResponse
[
{
"countryCode": 1,
"countryName": "Virgin Islands (U.S.)"
},
{
"countryCode": 7,
"countryName": "Russian Federation"
}
]
内部响应
[
{
"Type": "1",
"id": 2
},
{
"Type": "2",
"id": 3
}
]
最简洁直接的方法可能是async/await
方式。您可以手动宣传request
并用简单的延迟替换simple-rate-limiter
依赖:
'use strict';
const request = require('request');
function promisifiedRequest(options) {
return new Promise((resolve, reject) => {
request(options, (err, response, body) => {
if (err) reject(err);
else resolve(body);
});
});
}
function delay(ms) {
return new Promise((resolve) => { setTimeout(resolve, ms); });
}
const options = {
method: 'POST',
url: 'https://myendpoint/details',
headers: {
'cache-control': 'no-cache',
'Content-Type': 'application/json',
},
body: {
token: 'TOKEN',
method: 'countries',
},
json: true,
};
(async function main() {
try {
const countryResponse = await promisifiedRequest(options);
const innerRequests = [];
for (const entry of countryResponse) {
const innerOptions = {
method: 'POST',
url: 'https://myendpoint/details',
headers: {
'cache-control': 'no-cache',
'Content-Type': 'application/json',
},
body: {
token: 'TOKEN',
method: 'services',
countryCode: entry.countryCode,
},
json: true,
};
const innerRequest = promisifiedRequest(innerOptions);
innerRequests.push(innerRequest);
await delay(200);
}
const countryServices = await Promise.all(innerRequests);
console.log(JSON.stringify(countryServices, null, ''));
} catch (err) {
console.error(err);
}
})();
如果您需要更多背景或需要扩展应用程序(添加具有更复杂速率限制的并行请求),这些材料可能会有所帮助:
Stackoverflow: How do I return the response from an asynchronous call?
Stackoverflow: Why is my variable unaltered after I modify it inside of a function?
代码末尾的console.log
s不会等待forEach
触发的所有异步操作在运行之前完成。您需要引入某种机制来等待forEach触发的所有函数来完成其请求。
如果你想坚持使用回调,那么你可以看看使用each
的async方法,它将为你处理这种情况。
这个问题通常使用Promises和async/await来处理。如果您使用了promise based interface to request,假设有一个相当最新版本的Node.js(省略选项),您的示例将看起来像这样:
const request = require('request-promise');
async function run() {
const options = {};
const countryServices = [];
const countryResponse = await request(options);
for (const country of countryResponse) {
const innerOptions = {};
const innerResponse = await request(innerOptions);
countryServices.push(innerResponse);
}
console.log(countryServices);
}
run();
这比使用回调更清楚,for-of循环的行为与您期望的完全相同。