Javascript 节点异步循环-如何使此代码按顺序运行?
我知道有几个帖子是关于这一点的,但根据我发现的,这应该是正确的 我想在循环中发出http请求,并且我不希望循环在请求回调被触发之前进行迭代。我使用的异步库如下所示:Javascript 节点异步循环-如何使此代码按顺序运行?,javascript,node.js,asynchronous,Javascript,Node.js,Asynchronous,我知道有几个帖子是关于这一点的,但根据我发现的,这应该是正确的 我想在循环中发出http请求,并且我不希望循环在请求回调被触发之前进行迭代。我使用的异步库如下所示: const async = require("async"); const request = require("request"); let data = [ "Larry", "Curly", "Moe" ]; async.forEachOf(data, (result, idx, callback)
const async = require("async");
const request = require("request");
let data = [
"Larry",
"Curly",
"Moe"
];
async.forEachOf(data, (result, idx, callback) => {
console.log("Loop iterated", idx);
let fullUri = "https://jsonplaceholder.typicode.com/posts";
request({
url: fullUri
},
(err, res, body) => {
console.log("Request callback fired...");
if (err || res.statusCode !== 200) return callback(err);
console.log(result);
callback();
});
});
我看到的是:
Loop iterated 0
Loop iterated 1
Loop iterated 2
Request callback fired...
Curly
Request callback fired...
Larry
Request callback fired...
Moe
我需要看到的是:
Loop iterated 0
Request callback fired...
Curly
Loop iterated 1
Request callback fired...
Larry
Loop iterated 2
Request callback fired...
Moe
另外,如果有一种内置的方式来异步/等待做同样的事情?许诺而且可以删除异步库,这会更好
我已经看到了一些非常聪明的递归示例,但是当我将其用于更复杂的情况时,例如,每个循环都有多个请求调用,等等。我觉得这种方法很难遵循,而且可读性也不高。您可以完全抛弃异步,而很容易地使用异步/等待
承诺您的请求并使用async/Wait
只要把请求变成承诺,你就可以等待
更好的方法是,只使用已经使用本机承诺包装的请求
系列示例
从那时起,通过async/await进行扣篮:
平行示例
现在,上述解决方案的问题是速度慢——请求连续运行。大多数情况下,这并不理想
如果下一个请求不需要上一个请求的结果,只需继续并执行Promise.all即可触发并行请求
const users = [1, 2, 3, 4]
const pendingPromises = []
for (const idUser of users) {
// Here we won't `await` on *each and every* request.
// We'll just prepare it and push it into an Array
pendingPromises.push(rp('http://foo.com/users/' + idUser))
}
// Then we `await` on a a `Promise.all` of those requests
// which will fire all the prepared promises *simultaneously*,
// and resolve when all have been completed
const results = await Promise.all(pendingPromises)
错误处理
async/await中的错误处理是由普通的try..catch块提供的,为了简洁起见,我省略了它。您可以完全放弃async,很容易地选择async/await
承诺您的请求并使用async/Wait
只要把请求变成承诺,你就可以等待
更好的方法是,只使用已经使用本机承诺包装的请求
系列示例
从那时起,通过async/await进行扣篮:
平行示例
现在,上述解决方案的问题是速度慢——请求连续运行。大多数情况下,这并不理想
如果下一个请求不需要上一个请求的结果,只需继续并执行Promise.all即可触发并行请求
const users = [1, 2, 3, 4]
const pendingPromises = []
for (const idUser of users) {
// Here we won't `await` on *each and every* request.
// We'll just prepare it and push it into an Array
pendingPromises.push(rp('http://foo.com/users/' + idUser))
}
// Then we `await` on a a `Promise.all` of those requests
// which will fire all the prepared promises *simultaneously*,
// and resolve when all have been completed
const results = await Promise.all(pendingPromises)
错误处理
async/await中的错误处理由普通的try..catch块提供,为了简洁起见,我省略了它。如果要处理数千个URL,最好定义一个批大小并递归调用process函数来处理一个批 最好限制活动连接的数量,您可以使用限制活动连接或在特定时间内每秒仅5次的连接 最后但并非最不重要;如果你使用诺言,你就要确保当一个诺言被拒绝时,不会失去所有的成功。您可以捕获被拒绝的请求并返回失败类型对象,这样它就可以使用此失败类型进行解析 代码如下所示:
const async = require("async");
//lib comes from: https://github.com/amsterdamharu/lib/blob/master/src/index.js
const lib = require("lib");
const request = require("request");
const Fail = function(reason){this.reason=reason;};
const isFail = o=>(o&&o.constructor)===Fail;
const requestAsPromise = fullUri =>
new Promise(
(resolve,reject)=>
request({
url: fullUri
},
(err, res, body) => {
console.log("Request callback fired...");
if (err || res.statusCode !== 200) reject(err);
console.log("Success:",fullUri);
resolve([res,body]);
})
)
const process =
handleBatchResult =>
batchSize =>
maxFunction =>
urls =>
Promise.all(
urls.slice(0,batchSize)
.map(
url=>
maxFunction(requestAsPromise)(url)
.catch(err=>new Fail([err,url]))//catch reject and resolve with fail object
)
)
.then(handleBatch)
.catch(panic=>console.error(panic))
.then(//recursively call itself with next batch
_=>
process(handleBatchResult)(batchSize)(maxFunction)(urls.slice(batchSize))
);
const handleBatch = results =>{//this will handle results of a batch
//maybe write successes to file but certainly write failed
// you can retry later
const successes = results.filter(result=>!isFail(result));
//failed are the requests that failed
const failed = results.filter(isFail);
//To get the failed urls you can do
const failedUrls = failed.map(([error,url])=>url);
};
const per_batch_1000_max_10_active =
process (handleBatch) (1000) (lib.throttle(10));
//start the process
per_batch_1000_max_10_active(largeArrayOfUrls)
.then(
result=>console.log("Process done")
,err=>console.error("This should not happen:".err)
);
在handleBatchResult中,您可以将失败的请求存储到一个文件中,以便稍后重试const[error,uri]=failedResultItem;如果大量请求失败,您应该放弃
handleBatchResult之后有一个.catch,这是您的紧急模式,它不应该在那里失败,因此我建议您使用linux。如果要处理数千个URL,最好定义一个批大小,并递归调用process函数来处理一个批 最好限制活动连接的数量,您可以使用限制活动连接或在特定时间内每秒仅5次的连接 最后但并非最不重要;如果你使用诺言,你就要确保当一个诺言被拒绝时,不会失去所有的成功。您可以捕获被拒绝的请求并返回失败类型对象,这样它就可以使用此失败类型进行解析 代码如下所示:
const async = require("async");
//lib comes from: https://github.com/amsterdamharu/lib/blob/master/src/index.js
const lib = require("lib");
const request = require("request");
const Fail = function(reason){this.reason=reason;};
const isFail = o=>(o&&o.constructor)===Fail;
const requestAsPromise = fullUri =>
new Promise(
(resolve,reject)=>
request({
url: fullUri
},
(err, res, body) => {
console.log("Request callback fired...");
if (err || res.statusCode !== 200) reject(err);
console.log("Success:",fullUri);
resolve([res,body]);
})
)
const process =
handleBatchResult =>
batchSize =>
maxFunction =>
urls =>
Promise.all(
urls.slice(0,batchSize)
.map(
url=>
maxFunction(requestAsPromise)(url)
.catch(err=>new Fail([err,url]))//catch reject and resolve with fail object
)
)
.then(handleBatch)
.catch(panic=>console.error(panic))
.then(//recursively call itself with next batch
_=>
process(handleBatchResult)(batchSize)(maxFunction)(urls.slice(batchSize))
);
const handleBatch = results =>{//this will handle results of a batch
//maybe write successes to file but certainly write failed
// you can retry later
const successes = results.filter(result=>!isFail(result));
//failed are the requests that failed
const failed = results.filter(isFail);
//To get the failed urls you can do
const failedUrls = failed.map(([error,url])=>url);
};
const per_batch_1000_max_10_active =
process (handleBatch) (1000) (lib.throttle(10));
//start the process
per_batch_1000_max_10_active(largeArrayOfUrls)
.then(
result=>console.log("Process done")
,err=>console.error("This should not happen:".err)
);
在handleBatchResult中,您可以将失败的请求存储到一个文件中,以便稍后重试const[error,uri]=failedResultItem;如果大量请求失败,您应该放弃
在handleBatchResult之后有一个.catch,这是您的恐慌模式,它不应该在那里失败,所以我建议您使用linux。您不是指“每个系列”吗?每一个都是平行的。@JimBaldwin-太好了,谢谢!我错过了那个。这是可行的,但现在我们正在寻找一种不异步的方法。你不是说“每个系列”吗?每一个都是平行的。@JimBaldwin-太好了,谢谢!我错过了那个。这是可行的,但现在我们将研究一种不异步的方法。谢谢,Nicholas。我试过给你贴标签,但没法。此例程将是非常大的CSV上载的一部分。有多少未兑现的承诺就是太多了?如果我有100000张唱片呢?或者…数百万?呃,哦-创建这么多函数时,您可能会遇到调用堆栈限制问题-但回调风格的代码也会出现同样的问题。还有记忆呢?是否要将整个CSV加载到内存中?它合适吗?你可能需要调查一下。你到底需要做什么?我也这么想。现在使用小文件进行测试,但预期会更大
. 我已经通过multer将文件流式传输到API端点。然后我需要处理每一行,将其分割成几个记录,然后将这些记录插入分散在其他几个microservice端点上的表中……因此请求调用。很好。你能再问我一个问题吗?好的。非常感谢!可以在这里找到:谢谢,尼古拉斯。我试过给你贴标签,但没法。此例程将是非常大的CSV上载的一部分。有多少未兑现的承诺就是太多了?如果我有100000张唱片呢?或者…数百万?呃,哦-创建这么多函数时,您可能会遇到调用堆栈限制问题-但回调风格的代码也会出现同样的问题。还有记忆呢?是否要将整个CSV加载到内存中?它合适吗?你可能需要调查一下。你到底需要做什么?我也这么想。现在使用小文件进行测试,但预期会更大。我已经通过multer将文件流式传输到API端点。然后我需要处理每一行,将其分割成几个记录,然后将这些记录插入分散在其他几个microservice端点上的表中……因此请求调用。很好。你能再问我一个问题吗?好的。非常感谢!可以在这里找到: