zoukankan      html  css  js  c++  java
  • 在 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 = [1, 2, 3];
    
    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

    The reduce function

    2. 异步 reduce

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

    // utility function for sleeping
    const sleep = (n) => new Promise((res) => setTimeout(res, n));
    
    const arr = [1, 2, 3];
    
    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)

    Async reduce function

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

    3. 定时

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

    3.1 await memo last

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

    const arr = [1, 2, 3];
    
    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
    

     Async reduce with "await memo" last

    3.2 await memo first

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

    const arr = [1, 2, 3];
    
    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
    

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

    推荐阅读

    您的关注是莫大的鼓励 ❥(^_-)

  • 相关阅读:
    Jboss部署war以及获取Resource的真实路径
    命令行获取docker远程仓库镜像列表
    Hibernate5 与 Spring Boot2 最佳性能实践
    Spring Bean的一生
    Spring中统一相同版本的api请求路径的一些思考
    Java并发工具类CountDownLatch源码中的例子
    (转载)23种设计模式的uml图表示及通俗介绍
    GeoHash核心原理解析
    如何保证服务器的安全?
    小强升职记
  • 原文地址:https://www.cnblogs.com/luckrain7/p/12704805.html
Copyright © 2011-2022 走看看