JavaScript中Promise處理異步的并行與串行
一、異步的“并行”
同時處理,相互之間沒啥依賴
// 執(zhí)行FN1返回一個promise實例,實例中管理了一個異步編程的代碼,當(dāng)定時器到時間后,才會把實例的狀態(tài)改為成功 const fn1 = () => { return new Promise(resolve => { setTimeout(() => { resolve(1); }, 1000); }); }; const fn2 = () => { return new Promise(resolve => { setTimeout(() => { resolve(2); }, 2000); }); }; const fn3 = () => { return new Promise(resolve => { setTimeout(() => { resolve(3); }, 3000); }); }; // 異步的“并行”:同時處理,相互之間沒啥依賴 fn1().then(result => { console.log(result); }); fn2().then(result => { console.log(result); }); fn3().then(result => { console.log(result); });
并行中的綜合處理
一起發(fā)送多個請求(處理多個異步),但是需要等到所有異步都成功,我們再整體做啥事!!
語法:let promise = Promise.all([promise1,promise2,...]);
執(zhí)行Promise.all返回一個新的promise實例 @P
并且傳遞一個數(shù)組,數(shù)組中包含N多其它的promise實例
如果數(shù)組中的每一個promise實例最后都是成功的,則@P也將會是成功的,它的值也是一個數(shù)組,按照順序依次存儲各個promise實例的結(jié)果;但凡數(shù)組中的某個promsie實例是失敗的,則@P也是失敗的,值是當(dāng)前這個實例失敗的原因!
如果數(shù)組中有一項并不是promise實例(例如:是個100),則瀏覽器也會把其默認(rèn)變?yōu)橐粋€狀態(tài)是成功的promsie實例,值就是當(dāng)前項本身
let p = Promise.all([Promise.resolve(100), fn1(), 200, fn3(), fn2()]); //瀏覽器默認(rèn)會把200 變?yōu)橐粋€成功的promsie實例 p.then(results => { console.log(`成功:${results}`); // 成功:100,1,200,3,2 順序和最開始是一致的,不會考慮誰先成功 }).catch(reason => { console.log(`失?。?{reason}`); }); //new Error('xxx')不是promise實例=》Promise.resolve(new Error('xxx')) 值是Error對象 let p = Promise.all([Promise.resolve(100), fn1(), new Error('xxx'), Promise.reject(200), fn3(), fn2()]); p.then(results => { console.log(`成功:${results}`); }).catch(reason => { console.log(`失?。?{reason}`); //失?。?00 遇到錯誤直接返回 }); //=>等三個異步都成功做什么事情 所用時間3s Promise.all([fn1(), fn2(), fn3()]).then(results => { console.log(`三個異步都成功了,分別的結(jié)果:${results}`); });
二、異步的“串行”:
第一個異步成功才能發(fā)送第二個,第二個成功才能發(fā)送第三個....多個異步之間一般是有依賴的
2.1 then鏈機(jī)制處理
promise狀態(tài)是失敗,如果不用catch(或者onrejected)處理,控制臺會拋出異常:Uncaught (in promise) xxx,但是此異常不會阻礙下面代碼執(zhí)行!!
fn1().then(result => { console.log(`第一個成功:${result}`); return fn2(); }).then(result => { console.log(`第二個成功:${result}`); return fn3(); }).then(result => { console.log(`第三個成功:${result}`); }).catch(reason => { console.log(`只要其中一個失敗,直接順延到這里,其余剩下的請求就不發(fā)送了!`); });
2.2 真實項目中,想實現(xiàn)異步的串行,我們一般使用async+await
(async function () { let result = await fn1(); console.log(`第一個成功:${result}`); result = await fn2(); console.log(`第二個成功:${result}`); result = await fn3(); console.log(`第三個成功:${result}`); })();
2.3 promise.then(onfulfilled,onrejected) 在內(nèi)存中的執(zhí)行
首先,我們復(fù)習(xí)一下事件循環(huán)機(jī)制 EventLoop
同步代碼執(zhí)行,遇到一個異步任務(wù)
1. 先把其放在 WebAPI 進(jìn)行監(jiān)聽
2. 當(dāng)前異步任務(wù)監(jiān)聽到可以執(zhí)行了,則再把其放在EventQueue中,排隊等待執(zhí)行
同步任務(wù)執(zhí)行完,主線程空閑下來
1. 去EventQueue中找可執(zhí)行的微任務(wù),如果微任務(wù)中都執(zhí)行完了,再去找可執(zhí)行的宏任務(wù)「隊列:優(yōu)先級隊列 & 先進(jìn)先出」
2. 取到的任務(wù)都放在Stack中交給主線程去執(zhí)行
......
那么接下來我們來了解一下promise.then(onfulfilled,onrejected) 在內(nèi)存中的執(zhí)行
promise.then(onfulfilled,onrejected) 在內(nèi)存中的執(zhí)行
情況一:我此時已經(jīng)知道promise是成功還是失敗的
我們此時應(yīng)該去執(zhí)行onfulfilled或者onrejected,但是不是立即執(zhí)行,它是一個異步的微任務(wù)
首先把執(zhí)行對應(yīng)的方法這個事情放在WebAPI中監(jiān)聽,但是因為此時已經(jīng)知道狀態(tài)了,對應(yīng)的方法肯定可以執(zhí)行,所以緊接著把它挪至到EventQueue中「異步微任務(wù)隊列」等待執(zhí)行
情況二:此時的promise還是pending狀態(tài)
我們把onfulfilled/onrejected先存儲起來,只有當(dāng)后面,我們把實例的狀態(tài)修改為成功/失敗的時候,再取出之前存儲的方法,把其執(zhí)行「而且此時再執(zhí)行,還是個異步微任務(wù)」
還是要經(jīng)歷:WebAPI -> EventQueue
三、aysnc修飾符
aysnc最主要的作用就是:如果想在函數(shù)中使用await,則當(dāng)前函數(shù)必須基于async修飾
aysnc修飾符,讓函數(shù)的返回值成為一個promise實例 這樣就可以基于THEN鏈去處理了
如果函數(shù)自己本身就返回一個promise實例,則以自己返回的為主
如果函數(shù)自己本身沒有返回promise,則會把返回值變?yōu)橐粋€promise實例:狀態(tài)=> 成功 值=>返回值
如果函數(shù)執(zhí)行報錯則返回的實例狀態(tài)是失敗,值是報錯原因,但不影響下面代碼執(zhí)行
async function fn() { return 10; } fn().then(result => { console.log(result);//輸出10 }); async function fn() { return async function () { return 10; }; } fn().then(result => { // result:async function () {...} 函數(shù) return result(); }).then(result => { console.log(result); //10 });
四、await:等待
- 我們一般在其后面放promise實例 它會等待實例狀態(tài)為成功,再去執(zhí)行“當(dāng)前上下文”中 ,await下面的代碼
【如果promise實例管控的是一個異步編程,其實它是在等待異步成功,再執(zhí)行下面的代碼,類似于把異步改為同步的效果】
await 10 =>默認(rèn)轉(zhuǎn)為 await Promise.resolve(10)
- 如果后面放的不是promise實例,則瀏覽器默認(rèn)把其轉(zhuǎn)換為”狀態(tài)為成功,值就是這個值 " 的實例
const fn1 = () => { return new Promise(resolve => { setTimeout(() => { resolve(1); }, 1000); }); }; (async function () { let result = await fn1(); console.log(result); //下面代碼可以執(zhí)行,說明await后面的promise實例,它的狀態(tài)已經(jīng)是成功了,await的返回值就是當(dāng)前promise實例的值 console.log('OK'); })(); //=======================如果await后面的promise實例狀態(tài)是失敗的 const fn1 = () => { return new Promise((resolve, reject) => { setTimeout(() => { reject(0); }, 1000); }); }; (async function () { let result = await fn1(); //如果await后面的promise實例狀態(tài)是失敗的,則下面代碼永遠(yuǎn)都不會執(zhí)行了 console.log(result); console.log('OK');//如果是失敗的則這句話不會執(zhí)行 })(); //=================================================== (async function () { let a = await 10; //->Promise.resolve(10) console.log(a); //10 let b = await Promise.resolve(20); console.log(b); //20 try { let c = await Promise.reject(30); //Uncaught (in promise) 30 console.log(c); } catch (err) { console.log(err); //30 } console.log('OK'); //'OK' })();
await中的異步
await中的異步:當(dāng)前上下文,await下面的代碼執(zhí)行是異步微任務(wù)
情況1:await后面的promise實例我們已知是成功的
先把微任務(wù)放置在WebAPI中,但是知道是可以執(zhí)行的,則直接在挪至到EventQueue中等待執(zhí)行
情況2:await后面的promise實例還是pending狀態(tài)
此時我們把微任務(wù)放置在WebAPI中監(jiān)聽,等到后期promise實例是成功態(tài)后,再把它挪至到EventQueue中等待執(zhí)行即可
(async function () { let b = await Promise.resolve(20); console.log(b); })()
五、思考題
思考題1
console.log(1); setTimeout(() => { console.log(2); });//宏1 console.log(3); let p1 = new Promise(resolve => {//new Promise 立即會把executor函數(shù)執(zhí)行 是同步 console.log(4); resolve('A'); //執(zhí)行resolve P1的狀態(tài)是成功=》已知 console.log(5); }); console.log(6); p1.then(result => {//.then是異步 已知狀態(tài) 放進(jìn)WebAPI中監(jiān)聽 但是因為是已知狀態(tài) 方法肯定執(zhí)行 則從WebAPI監(jiān)聽隊列挪到EventQueue中等待 console.log(result); //微1 }); console.log(7); let p2 = new Promise(resolve => { //立即執(zhí)行executor函數(shù) setTimeout是異步則會放進(jìn)WebAPI監(jiān)聽 宏2 setTimeout(() => { resolve('B'); console.log(10); //執(zhí)行宏2=>@1 更改了p2的狀態(tài) @2之前存儲的.then方法執(zhí)行 }); }); console.log(8); p2.then(result => {//p2的狀態(tài)是未知則會先存起來 console.log(result); }); console.log(9);
思路及圖解
思考題2
基于事件綁定屬于異步宏任務(wù)
let body = document.body; body.addEventListener('click', function () { Promise.resolve().then(() => { console.log(1); }); console.log(2); }); body.addEventListener('click', function () { Promise.resolve().then(() => { console.log(3); }); console.log(4); });
思路及圖解
思考題3
async function async1() { console.log('async1 start'); await async2(); console.log('async1 end'); } async function async2() { console.log('async2'); } console.log('script start'); setTimeout(function () { console.log('setTimeout'); }, 0) async1(); new Promise(function (resolve) { console.log('promise1'); resolve(); }).then(function () { console.log('promise2'); }); console.log('script end');
思路及圖解
總結(jié)
到此這篇關(guān)于JavaScript中Promise處理異步的并行與串行的文章就介紹到這了,更多相關(guān)JS Promise異步并行與串行內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Echart結(jié)合圓形實現(xiàn)儀表盤的繪制詳解
EChart開源來自百度商業(yè)前端數(shù)據(jù)可視化團(tuán)隊,基于html5?Canvas,是一個純Javascript圖表庫,提供直觀,生動,可交互,可個性化定制的數(shù)據(jù)可視化圖表。本文將利用EChart實現(xiàn)儀表盤的繪制,感興趣的可以學(xué)習(xí)一下2022-03-03JS隨機(jī)生成不重復(fù)數(shù)據(jù)的實例方法
這篇文章介紹了JS隨機(jī)生成不重復(fù)數(shù)據(jù)的實例方法,有需要的朋友可以參考一下2013-07-07簡單實用的js調(diào)試logger組件實現(xiàn)代碼
開發(fā)js組件的時間調(diào)試總是麻煩的,最常用的就是用alert或者debugger來測試js的運(yùn)行狀態(tài)。2010-11-11