如何以順序或并行方式運(yùn)行異步循環(huán)?
在對(duì)循環(huán)進(jìn)行異步處理之前,我想提醒您如何編寫經(jīng)典的同步循環(huán)。
同步循環(huán)
很久以前我也是這樣寫For循環(huán):
for (var i=0; i < array.length; i++) {
var item = array[i];
// do something with item
}
這樣寫很好,速度很快,但是有很多可讀性和維護(hù)問題。我們可以用它更好的版本:
array.forEach((item) => {
// do something with item
});
JS語言在開發(fā)中運(yùn)行很快,我們還有更多的功能和新語法,其中一個(gè)是我最常用的 async/await。我現(xiàn)在也經(jīng)常在用,有時(shí)在異步處理數(shù)組的里數(shù)據(jù)時(shí)會(huì)出現(xiàn)這個(gè)問題。
異步循環(huán)
如何在循環(huán)中使用 await ?讓我們編寫異步函數(shù)并 await 每個(gè)任務(wù)。
async function processArray(array) {
array.forEach(item => {
// define synchronous anonymous function
// IT WILL THROW ERROR!
await func(item);
})
}
這段代碼會(huì)出現(xiàn)一個(gè)語法錯(cuò)誤,因?yàn)槲覀儾荒茉谕胶瘮?shù)里使用 await 。“processArray”是一個(gè)異步函數(shù),但是我們?cè)谶@個(gè)匿名函數(shù)里用到的 forEach 是同步的。
1.不需要等待結(jié)果
如何修復(fù)之前的問題?我們可以這樣異步定義匿名函數(shù):
async function processArray(array) {
array.forEach(async (item) => {
await func(item);
})
console.log('Done!');
}
但是 forEach 不會(huì)等到所有的步驟都完成,它只會(huì)執(zhí)行任務(wù)和執(zhí)行下一步。作為證明我們可以這樣寫一個(gè)簡單例子:
function delay() {
return new Promise(resolve => setTimeout(resolve, 300));
}
async function delayedLog(item) {
// notice that we can await a function
// that returns a promise
await delay();
console.log(item);
}
async function processArray(array) {
array.forEach(async (item) => {
await delayedLog(item);
})
console.log('Done!');
}
processArray([1, 2, 3]);
結(jié)果輸出為:
Done!
1
2
3
如果不需要等結(jié)果這樣寫是ok的,但是在大多數(shù)案例里這不是個(gè)很好的邏輯。
2. 線性處理數(shù)組
要等待結(jié)果,我們應(yīng)該返回到老式的 for 循環(huán),但這一次為了更好的可讀性我們可以使用現(xiàn)代寫法 for..of。
sync function processArray(array) {
for (const item of array) {
await delayedLog(item);
}
console.log('Done!');
}
結(jié)果輸出:
1
2
3
Done!
該代碼將依次處理每一項(xiàng)。但是我們可以使用并行運(yùn)行。
3.并行處理數(shù)組
我們可以稍微修改下代碼然后并行運(yùn)行:
async function processArray(array) {
// map array to promises
const promises = array.map(delayedLog);
// wait until all promises are resolved
await Promise.all(promises);
console.log('Done!');
}
這段代碼將并行運(yùn)行許多delayLog 任務(wù)。但是對(duì)于非常大的數(shù)組要小心(并行的任務(wù)太多對(duì)CPU或內(nèi)存來說可能比較吃力)。
也不要混淆“并行”與真正的線程和并行。該代碼不能保證真正的并行執(zhí)行。這取決于您的 item函數(shù)(在本演示中是delayedLog)。網(wǎng)絡(luò)請(qǐng)求、webworker 和其他一些任務(wù)可以并行執(zhí)行。
感謝閱讀!