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