在 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 函數很容易轉換為異步函數,但是要弄清楚並行性可能很棘手。幸運的是,它很少破壞任何東西,但是在一些資源密集型或速率受限的操作中,了解如何調用函數至關重要。

推薦閱讀

您的關注是莫大的鼓勵 ❥(^_-)


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM