你是否遇到過這樣的情況:你期望你的 map 函數(shù)返回一個(gè)已解決/等待的值,但它卻返回了一個(gè)包含多個(gè) promises 的列表。例如:
const result = ids.map(async (id) => {
return await fetch(id);
});
// 期望結(jié)果:[{ name: "Adam" }, { name: "Linda" }]
// 實(shí)際結(jié)果:[Promise {<pending>}, Promise {<pending>}, ...]
你并不孤單。我在配對編程中也遇到過這種情況幾次。本文將展示如何在 async/await 操作中進(jìn)行映射。
準(zhǔn)備好了嗎?讓我們開始。
問題
想象一下:你拿到了一個(gè)需要異步處理的項(xiàng)目數(shù)組。也許這些項(xiàng)目代表 ID,你需要為每個(gè) ID 獲取數(shù)據(jù)。這是一個(gè)常見的錯(cuò)誤:
const ids = [1, 2, 3, 4, 5];
const fetchData = async (id) => {
// 模擬異步獲取操作
return `data for ${id}`;
};
const processItems = async () => {
const result = ids.map(async (id) => {
return await fetchData(id);
});
console.log(result); // [Promise {<pending>}, Promise {<pending>}, ...]
};
processItems();
好的,這里出了什么問題呢??? map
函數(shù)返回了一個(gè) promises 數(shù)組,并沒有等待它們解決。這不是我們想要的,并且在稍后嘗試使用 result
時(shí)可能會(huì)導(dǎo)致混亂。
for…of 循環(huán)解決方案
一種方法是使用 for...of 循環(huán)。如果你的異步操作需要按順序發(fā)生而不是并行執(zhí)行,這將非常有用。
const processItemsSequentially = async () => {
const result = [];
for (const id of ids) {
const data = await fetchData(id);
result.push(data);
}
console.log(result); // ['data for 1', 'data for 2', ...]
};
processItemsSequentially();
如果你的目標(biāo)是順序執(zhí)行,這將更容易閱讀和理解,但要小心——這種方法可能會(huì)更慢,因?yàn)槊總€(gè)操作都等待前一個(gè)操作完成。
Promise.all 解決方案
讓我們使用 Promise.all 和 Array.prototype.map() 來清理這個(gè)問題。這個(gè)巧妙的方法可以將我們的 promises 數(shù)組包裝成一個(gè)單一的 promise,當(dāng)所有 promises 解決時(shí),它將解決。
const processItems = async () => {
const result = await Promise.all(
ids.map((id) => {
return fetchData(id);
})
);
console.log(result); // ['data for 1', 'data for 2', ...]
};
processItems();
太好了!現(xiàn)在我們正在高效地工作。promises 數(shù)組被包裝在一個(gè)單一的 promise 中,它將用結(jié)果解決。好多了,并且是并發(fā)運(yùn)行的!然而,有一個(gè)問題。并發(fā)運(yùn)行 promises(例如,有 1000 個(gè)項(xiàng)目)并不總是意味著快速。它可能會(huì)變慢,并可能導(dǎo)致內(nèi)存問題。
提示:你可以選擇在這個(gè)例子中使用 promise.all() 或 promise.allSettled()。
使用 p-map 的更清晰解決方案
最后,讓我們看看一個(gè)更好的并發(fā)映射方法,同時(shí)限制應(yīng)該同時(shí)運(yùn)行的 promises 數(shù)量。為此,我們將使用 npm 上的 p-map。你可以使用 npm install p-map
將其添加到你的項(xiàng)目中。
它與 Promise.all()
不同,因?yàn)槟憧梢钥刂撇l(fā)限制,并決定在出現(xiàn)錯(cuò)誤時(shí)是否停止迭代。以下是我們定義的 processItems()
函數(shù)的樣子:
import pMap from 'p-map';
const ids = [1, 2, 3, 4, 5];
const fetchData = async (id: number) => {
// 模擬異步獲取操作
return `data for ${id}`;
};
const processItems = async () => {
const result = await pMap(ids, (id) => fetchData(id), { concurrency: 2 });
console.log(result); // ['data for 1', 'data for 2', ...]
};
processItems();
雖然我們在這里使用了不同的語法,但這個(gè)版本是簡潔有效的。通過設(shè)置并發(fā)限制,這種模式避免了在有大量數(shù)據(jù)時(shí)系統(tǒng)過載,并且我們可以控制在出現(xiàn)錯(cuò)誤時(shí)是否停止或繼續(xù)。有關(guān) p-map
的更多選項(xiàng),請查看 GitHub 上的文檔。
結(jié)論
就是這樣,朋友們!我們探討了在映射 promises 時(shí)的一個(gè)常見錯(cuò)誤,并涵蓋了三種有效的解決方案:
- 使用
for...of
進(jìn)行順序操作 ?? - 使用
Promise.all
進(jìn)行并行執(zhí)行 ????
我希望這能幫助你成為一個(gè)Promise映射專家!編碼愉快!??
原文地址:https://www.telerik.com/blogs/mapping-promises-javascript
閱讀原文:原文鏈接
該文章在 2024/12/31 11:37:53 編輯過