JavaScript中Promise處理異步的并行與串行
一、異步的“并行”
同時處理,相互之間沒啥依賴
// 執(zhí)行FN1返回一個promise實例,實例中管理了一個異步編程的代碼,當定時器到時間后,才會把實例的狀態(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實例的結果;但凡數(shù)組中的某個promsie實例是失敗的,則@P也是失敗的,值是當前這個實例失敗的原因!
如果數(shù)組中有一項并不是promise實例(例如:是個100),則瀏覽器也會把其默認變?yōu)橐粋€狀態(tài)是成功的promsie實例,值就是當前項本身
let p = Promise.all([Promise.resolve(100), fn1(), 200, fn3(), fn2()]); //瀏覽器默認會把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(`三個異步都成功了,分別的結果:${results}`);
}); 二、異步的“串行”:
第一個異步成功才能發(fā)送第二個,第二個成功才能發(fā)送第三個....多個異步之間一般是有依賴的
2.1 then鏈機制處理
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í)行
首先,我們復習一下事件循環(huán)機制 EventLoop
同步代碼執(zhí)行,遇到一個異步任務
1. 先把其放在 WebAPI 進行監(jiān)聽
2. 當前異步任務監(jiān)聽到可以執(zhí)行了,則再把其放在EventQueue中,排隊等待執(zhí)行
同步任務執(zhí)行完,主線程空閑下來
1. 去EventQueue中找可執(zhí)行的微任務,如果微任務中都執(zhí)行完了,再去找可執(zhí)行的宏任務「隊列:優(yōu)先級隊列 & 先進先出」
2. 取到的任務都放在Stack中交給主線程去執(zhí)行
......
那么接下來我們來了解一下promise.then(onfulfilled,onrejected) 在內(nèi)存中的執(zhí)行
promise.then(onfulfilled,onrejected) 在內(nèi)存中的執(zhí)行
情況一:我此時已經(jīng)知道promise是成功還是失敗的
我們此時應該去執(zhí)行onfulfilled或者onrejected,但是不是立即執(zhí)行,它是一個異步的微任務
首先把執(zhí)行對應的方法這個事情放在WebAPI中監(jiān)聽,但是因為此時已經(jīng)知道狀態(tài)了,對應的方法肯定可以執(zhí)行,所以緊接著把它挪至到EventQueue中「異步微任務隊列」等待執(zhí)行
情況二:此時的promise還是pending狀態(tài)
我們把onfulfilled/onrejected先存儲起來,只有當后面,我們把實例的狀態(tài)修改為成功/失敗的時候,再取出之前存儲的方法,把其執(zhí)行「而且此時再執(zhí)行,還是個異步微任務」
還是要經(jīng)歷:WebAPI -> EventQueue
三、aysnc修飾符
aysnc最主要的作用就是:如果想在函數(shù)中使用await,則當前函數(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í)行“當前上下文”中 ,await下面的代碼
【如果promise實例管控的是一個異步編程,其實它是在等待異步成功,再執(zhí)行下面的代碼,類似于把異步改為同步的效果】
await 10 =>默認轉為 await Promise.resolve(10)
- 如果后面放的不是promise實例,則瀏覽器默認把其轉換為”狀態(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的返回值就是當前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)是失敗的,則下面代碼永遠都不會執(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中的異步:當前上下文,await下面的代碼執(zhí)行是異步微任務
情況1:await后面的promise實例我們已知是成功的
先把微任務放置在WebAPI中,但是知道是可以執(zhí)行的,則直接在挪至到EventQueue中等待執(zhí)行
情況2:await后面的promise實例還是pending狀態(tài)
此時我們把微任務放置在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) 放進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是異步則會放進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
基于事件綁定屬于異步宏任務
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'); 思路及圖解

總結
到此這篇關于JavaScript中Promise處理異步的并行與串行的文章就介紹到這了,更多相關JS Promise異步并行與串行內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
簡單實用的js調(diào)試logger組件實現(xiàn)代碼
開發(fā)js組件的時間調(diào)試總是麻煩的,最常用的就是用alert或者debugger來測試js的運行狀態(tài)。2010-11-11

