JavaScript 中的异步并发
作者:互联网
我最近的任务是构建一个微服务,该服务需要发出数千个HTTP请求并处理响应。为了创建这种情况的简单模拟,我将使用一个 fake 函数,该函数接受任意值并返回解析为包含值和间隔的对象:request
Promise
const {promisify} = require('util') const sleep = promisify(setTimeout)const request = async(data) => { let time = Math.random() * 1000 await sleep(time) return {data, time} }
我有一堆包含请求参数数据的记录,所以我做的第一件事就是查询这些记录。对于此示例,我们将使用整数数组来模拟记录。我的第一直觉(我经验不足)是遍历记录数组,发出每个HTTP请求并将结果推送到新数组:
async function main() { const records = Array.from(new Array(10)).map((e, i) => i) let responses = [] console.time('Inline') for (let i = 0; i < records.length; i++) { let response = await request(records[i]) responses.push(response) } console.log(JSON.stringify(responses, null, 2)) console.timeEnd('Inline') }
这工作正常,但我们必须在发出下一个请求之前等待每个响应,这意味着这可能需要长达 10 秒才能执行。结果如下所示:
[ { "data": 0, "time": 48.95140139293264 }, { "data": 1, "time": 351.42969859007377 }, ... ] Inline: 5210.4460449ms
5秒是一段很长的时间。现在让我们看看如何同时执行这些请求。我们可以将每个调用推送到一个数组中,并使用 Promise.all() 等待它们全部解析:request
async function main() { const records = Array.from(new Array(10)).map((e, i) => i) console.time('Concurrent') let promises = [] for (let i = 0; i < records.length; i++) { promises.push(request(records[i])) } let responses = await Promise.all(promises) console.log(JSON.stringify(responses, null, 2)) console.timeEnd('Concurrent') }
瞧,我们刚刚加快了服务速度!结果如下所示:records.length
[ { "data": 0, "time": 160.08417354131944 }, { "data": 1, "time": 560.08495847237463 }, ..., { "data": 9, "time": 223.39482395749209 } ] Concurrent: 560.08495847237463ms
使用这种非阻塞方法会在所有承诺全部完成后立即获取它们的结果,因此总执行时间仅比最慢的 HTTP 请求大几毫秒。此相同技术可用于任何异步操作集合。responses
只是为了好玩,让我们在一行中使用Array.map:
let responses = await Promise.all(records.map(record => request(record)))
我希望这有帮助,快乐编码。