likes
comments
collection
share

在 Array.reduce 中正确使用 async

作者站长头像
站长
· 阅读数 30

在 Array.reduce 中正确使用 async

如何使用带有reduce的Promise以及如何在串行和并行处理之间进行选择

本文译自How to use async functions with Array.reduce in Javascript - Tamás Sallai

第一篇文章中,我们介绍了async / await 如何帮助执行异步命令,但在异步处理集合时却无济于事。在本文中,我们将研究reduce函数,它是功能最丰富的集合函数,因为它可以模拟所有其他函数。

1. Array.reduce

Reduce 迭代地构造一个值并返回它,它不一定是一个集合。这就是名字的来源,因为它减少了收集到的值。

迭代函数获取先前的结果( memo 在下面的示例中调用)以及当前值e

以下函数对元素进行求和,从0开始(第二个参数 reduce):

const arr = [123];const syncRes = arr.reduce((memo, e) => { return memo + e;}, 0);console.log(syncRes);// 6
memo e 结果
0(初始) 1个 1个
1个 2 3
3 3 (最终结果)6
在 Array.reduce 中正确使用 asyncThe reduce function

2. 异步 reduce

异步版本几乎相同,但每次迭代都会返回一个Promise,因此 memo 将是先前结果的Promise。迭代函数需要 await 它才能计算下一个结果:

// utility function for sleepingconst sleep = (n) => new Promise((res) => setTimeout(res, n));const arr = [123];const asyncRes = await arr.reduce(async (memo, e) => { await sleep(10); return (await memo) + e;}, 0);console.log(asyncRes);// 6
memo e 结果
0(初始) 1 Promise (1)
Promise (1) 2 Promise (3)
Promise (3) 3 (最终结果)Promise (6)
在 Array.reduce 中正确使用 asyncAsync reduce function

使用的结构async (memo, e) => await memoreduce可以处理任何异步功能,并且可以对其进行await编辑。

3. 定时

当在 reduce 中并发时有一个有趣的属性。在同步版本中,元素被一对一处理,这并不奇怪,因为它们依赖于先前的结果。但是,当异步 reduce 运行时,所有迭代函数将开始并行运行,await memo仅在需要时才等待上一个结果。

3.1 await memo last

在上面的示例中,所有 sleep 并行执行 ,因为await memo使得函数等待上一个函数完成后执行。

const arr = [123];const startTime = new Date().getTime();const asyncRes = await arr.reduce(async (memo, e) => { await sleep(10); return (await memo) + e;}, 0);console.log(`Took ${new Date().getTime() - startTime} ms`);// Took 11-13 ms
在 Array.reduce 中正确使用 async Async reduce with "await memo" last

3.2 await memo first

但是当await memo最先出现时,这些函数按顺序运行:

const arr = [123];const startTime = new Date().getTime();const asyncRes = await arr.reduce(async (memo, e) => { await memo; await sleep(10); return (await memo) + e;}, 0);console.log(`Took ${new Date().getTime() - startTime} ms`);// Took 36-38 ms
在 Array.reduce 中正确使用 asyncAsync reduce with "await memo" first

这种行为通常不是问题,这意味着不依赖于先前结果的所有内容都将立即被计算出来,只有依赖部分正在等待先前的值。

3.3 当并行很重要时

但是在某些情况下,提前做一些事情可能是不可行的。

例如,我有一段代码可以打印不同的PDF,并使用 pdf-lib 库将它们拼接到一个文件中。

实现 printPDF 并行运行资源密集型功能:

const result = await printingPages.reduce(async (memo, page) => { const pdf = await PDFDocument.load(await printPDF(page)); const pdfDoc = await memo; (await pdfDoc.copyPages(pdf, pdf.getPageIndices()))  .forEach((page) => pdfDoc.addPage(page)); return pdfDoc;}, PDFDocument.create());

我注意到当我有很多页面要打印时,它将占用过多的内存并减慢整个过程。

一个简单的更改,使 printPDF 调用等待上一个调用完成:

const result = await printingPages.reduce(async (memo, page) => { const pdfDoc = await memo; const pdf = await PDFDocument.load(await printPDF(page)); (await pdfDoc.copyPages(pdf, pdf.getPageIndices()))  .forEach((page) => pdfDoc.addPage(page)); return pdfDoc;}, PDFDocument.create());

4. 结论

reduce 函数很容易转换为异步函数,但是要弄清楚并行性可能很棘手。幸运的是,它很少破坏任何东西,但是在一些资源密集型或速率受限的操作中,了解如何调用函数至关重要。

推荐阅读

在 Array.reduce 中正确使用 async您的关注是莫大的鼓励 ❥(^_-)
转载自:https://juejin.cn/post/6844904127995068424
评论
请登录