JavaScript實(shí)現(xiàn)異步任務(wù)循環(huán)順序執(zhí)行詳解
需求場(chǎng)景:數(shù)組的元素作為異步任務(wù)的參數(shù),循環(huán)遍歷該數(shù)組,并執(zhí)行異步任務(wù)。
一、錯(cuò)誤的實(shí)現(xiàn)
簡(jiǎn)單的錯(cuò)誤實(shí)現(xiàn)
// 異步任務(wù)的參數(shù)數(shù)組
const arr = [1, 2, 3, 4];
// 異步任務(wù)函數(shù)
function task(params, callback) {
setTimeout(() => {
if (!!callback) {
callback(params);
}
}, 1000);
}
// 循環(huán)遍歷異步任務(wù)的參數(shù)數(shù)組,并執(zhí)行異步任務(wù)
console.time("Test code");
arr.forEach((item, index) => {
task(item, (ret) => {
console.log("ret", ret);
console.timeEnd("Test code");
if (index + 1 < arr.length) {
console.time("Test code");
}
});
});
執(zhí)行結(jié)果輸出:

由上圖可知,所有異步任務(wù)的執(zhí)行是同時(shí)開始,并同時(shí)結(jié)束的,并未按順序先后執(zhí)行。
使用 Promise.all 的錯(cuò)誤實(shí)現(xiàn)
Promise.all() 是一個(gè)用于并行執(zhí)行多個(gè) Promise 的方法,當(dāng)所有的 Promise 都成功執(zhí)行后,它返回一個(gè)包含所有 Promise 結(jié)果的數(shù)組,如果其中任何一個(gè) Promise 失敗或出錯(cuò),它將直接跳轉(zhuǎn)到 catch 塊中返回一個(gè) rejected 狀態(tài)的 Promise。
// 異步任務(wù)的參數(shù)數(shù)組
const arr = [1, 2, 3, 4];
// 異步任務(wù)函數(shù)
function task(params, callback) {
setTimeout(() => {
if (!!callback) {
callback(params);
}
}, 1000);
}
const tasks = [];
// 循環(huán)遍歷異步任務(wù)的參數(shù)數(shù)組,并執(zhí)行異步任務(wù)
console.time("Test code");
arr.forEach((item, index) => {
tasks.push(
new Promise((resolve) => {
task(item, (ret) => {
console.log("ret", ret);
console.timeEnd("Test code");
if (index + 1 < arr.length) {
console.time("Test code");
}
resolve(ret);
});
})
);
});
Promise.all(tasks)
.then((values) => {
console.log(values);
})
.catch((error) => {
console.error(error);
});
執(zhí)行結(jié)果輸出:

由上圖可知,循環(huán)中的所有異步任務(wù)的執(zhí)行是并行執(zhí)行的,并未按順序先后執(zhí)行。因?yàn)?Promise.all() 方法的執(zhí)行順序是并行執(zhí)行的,而不是按照 Promise 在數(shù)組中的順序執(zhí)行的。
二、正確的實(shí)現(xiàn)
// 異步任務(wù)的參數(shù)數(shù)組
const arr = [1, 2, 3, 4];
// 異步任務(wù)函數(shù)
function task(params, callback) {
setTimeout(() => {
if (!!callback) {
callback(params);
}
}, 1000);
}
const tasks = [];
console.time("Test code");
arr.forEach((item, index) => {
tasks.push(function () {
return new Promise((resolve) => {
task(item, (ret) => {
console.log("ret", ret);
console.timeEnd("Test code");
if (index + 1 < arr.length) {
console.time("Test code");
}
resolve(ret);
});
});
});
});
// 定義一個(gè)遞歸函數(shù)來依次執(zhí)行任務(wù)
function runTasks(index) {
if (index >= tasks.length) {
// 如果所有任務(wù)都已經(jīng)執(zhí)行完畢,返回一個(gè) resolved 的 Promise
return Promise.resolve();
}
// 執(zhí)行當(dāng)前任務(wù),然后遞歸執(zhí)行下一個(gè)任務(wù)
return tasks[index]().then(function () {
return runTasks(index + 1);
});
}
// 調(diào)用遞歸函數(shù)來執(zhí)行任務(wù)
runTasks(0)
.then(function () {
console.log("All tasks are done!");
})
.catch(function (error) {
console.error(error);
});
執(zhí)行結(jié)果輸出:

到此這篇關(guān)于JavaScript實(shí)現(xiàn)異步任務(wù)循環(huán)順序執(zhí)行詳解的文章就介紹到這了,更多相關(guān)JavaScript異步任務(wù)循環(huán)順序執(zhí)行內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
JavaScript拆分字符串時(shí)產(chǎn)生空字符的解決方案
使用JavaScript的split方法拆分字符串時(shí)出現(xiàn)一些空字符串"",尤其是當(dāng)使用正則表達(dá)式作為分隔符的時(shí)候。那么,產(chǎn)生這些空字符串的原因是什么?又該如何來處理呢,這就是今天我們要探討的問題2014-09-09
Javascript中獲取瀏覽器類型和操作系統(tǒng)版本等客戶端信息常用代碼
跟蹤一些最基本的客戶端訪問信息,這里將一些公用的代碼總結(jié)下來,需要的朋友可以參考下2016-06-06
詳解CocosCreator系統(tǒng)事件是怎么產(chǎn)生及觸發(fā)的
這篇文章主要介紹了CocosCreator系統(tǒng)事件是怎么產(chǎn)生及觸發(fā)的,雖然內(nèi)容不少,但是只要一點(diǎn)點(diǎn)抽絲剝繭,具體分析其內(nèi)容,就會(huì)豁然開朗2021-04-04
javascript 限制輸入和粘貼(IE和火狐3.x下測(cè)試通過)
限制輸入和粘貼的js代碼2008-11-11
可以用來搜索當(dāng)前頁(yè)面內(nèi)容的js代碼
搜索頁(yè)面內(nèi)容的js代碼,不過這效率不是很高,大篇幅內(nèi)容不建議使用。2009-12-12
JS實(shí)現(xiàn)點(diǎn)擊顏色塊切換指定區(qū)域背景顏色的方法
這篇文章主要介紹了JS實(shí)現(xiàn)點(diǎn)擊顏色塊切換指定區(qū)域背景顏色的方法,涉及javascript操作cookie及背景色的技巧,具有一定參考借鑒價(jià)值,需要的朋友可以參考下2015-02-02
javascript typeof的用法與typeof運(yùn)算符介紹[詳細(xì)]
下面是對(duì)于typeof運(yùn)算符的詳細(xì)介紹跟typeof的一些用法,分析,學(xué)習(xí)typeof的朋友,看完了,這篇應(yīng)該能有所收獲。2008-10-10

