promise和await async進階
寫部落格,既能梳理了專題知識,又加深記憶和理解,好了,不廢話,正文馬上開始。
1. promise作用
作用:promise解決回撥地獄的問題
2.promise基本用法
一定要記得 new Promise(executor) 的 executor 是馬上執行的;
promise、then、finally都是微任務;
let myPromise = new promise(function(resolved,rejected){})
Promise結合setTimeout
微任務包括:MutationObserver、Promise.then()或catch()、Promise為基礎開發的其它技術,比如fetch API、V8的垃圾回收過程、Node獨有的process.nextTick。
巨集任務包括:script 、setTimeout、setInterval 、setImmediate 、I/O 、UI rendering。
event loop它的執行順序:
- 一開始整個指令碼作為一個巨集任務執行
- 執行過程中同步程式碼直接執行,巨集任務進入巨集任務佇列,微任務進入微任務佇列
- 當前巨集任務執行完出隊,檢查微任務列表,有則依次執行,直到全部執行完
- 執行瀏覽器UI執行緒的渲染工作
- 檢查是否有Web Worker任務,有則執行
- 執行完本輪的巨集任務,回到2,依此迴圈,直到巨集任務和微任務佇列都為空
按照event loop規則就能很快輸出結果
Promise中的then、catch、finally
- Promise的狀態一經改變就不能再改變;
- .then和.catch都會返回一個新的Promise;
- catch不管被連線到哪裡,都能捕獲上層未捕捉過的錯誤;
- 在Promise中,返回任意一個非 promise 的值都會被包裹成; promise物件,例如return 2會被包裝為return Promise.resolve(2);
async function async1 () {
return 'async1 end'
}
console.log('srcipt start')
async1().then(res => console.log(res))
console.log('srcipt end')
'srcipt start'
'srcipt end'
'async1 end'
複製程式碼
- Promise 的 .then 或者.catch可以被呼叫多次,但如果Promise內部的狀態一經改變,並且有了一個值,那麼後續每次呼叫.then或者.catch的時候都會直接拿到該值;
- .then 或者 .catch 中 return 一個 error; 物件並不會丟擲錯誤,所以不會被後續的 .catch 捕獲;
- .then 或 .catch 返回的值不能是 promise 本身,否則會造成死迴圈;
- .then 或者 .catch 的引數期望是函式,傳入非函式則會發生值透傳;
Promise.resolve(1)
.then(2)
.then(Promise.resolve(3))
.then(console.log)
//1 因為發生了透傳
複製程式碼
- .then方法是能接收兩個引數的,第一個是處理成功的函式,第二個是處理失敗的函式,再某些時候你可以認為catch是.then第二個引數的簡便寫法;
.finally方法也是返回一個Promise
,他在Promise結束的時候,無論結果為resolved還是rejected,都會執行裡面的回撥函式。
就像是這裡的finally()會等promise1().then()執行完才會將finally()加入微任務佇列
其實你只要記住它三個很重要的知識點就可以了:
- .finally()方法不管Promise物件最後的狀態如何都會執行
- .finally()方法的回撥函式不接受任何的引數,也就是說你在.finally()函式中是沒法知道Promise最終的狀態是resolved還是rejected的
它最終返回的預設會是一個上一次的Promise物件值
,不過如果丟擲的是一個異常則返回異常的Promise物件。
const p1 = new Promise((resolve) => {
setTimeout(() => {
resolve('resolve3');
console.log('timer1')
}, 0)
resolve('resovle1');
resolve('resolve2');
}).then(res => {
console.log(res)
setTimeout(() => {
console.log(p1)
}, 1000)
}).finally(res => {
console.log('finally', res)
})
'resolve1'
'finally' undefined
'timer1'
Promise{<resolved>: undefined}
分析:
- Promise的狀態一旦改變就無法改變
- finally不管Promise的狀態是resolved還是rejected都會執行,且它的回撥函式是接收不到Promise的結果的,所以finally()中的res是一個迷惑項(類似3.10)。
- 最後一個定時器打印出的p1其實是.finally的返回值,我們知道.finally的返回值如果在沒有丟擲錯誤的情況下預設會是上一個Promise的返回值,
- 而這道題中.finally上一個Promise是.then(),但是這個.then()並沒有返回值,所以p1打印出來的Promise的值會是undefined,如果你在定時器的下面加上一個return 1,返回是1。
複製程式碼
Promise中的all、race
- Promise.all()的作用是
接收一組非同步任務
,然後並行執行非同步任務,並且在所有非同步操作執行完後才執行回撥。 - .race()的作用也是接收一組非同步任務,然後並行執行非同步任務,只保留取
第一個執行完成的非同步操作
的結果,其他的方法仍在執行,不過執行結果會被拋棄。 - Promise.all().then()結果中陣列的順序和Promise.all()接收到的陣列順序一致。
- all和race傳入的陣列中如果有會丟擲異常的非同步任務,那麼
只有最先丟擲的錯誤會被捕獲
,並且是被then的第二個引數或者後面的catch捕獲
;但並不會影響陣列中其它的非同步任務的執行。
3. await async
正常情況下,async中的await命令是一個Promise物件,返回該物件的結果。
但如果不是Promise物件的話,就會直接返回對應的值,相當於Promise.resolve()
async function fn () {
// return await 123
// 等同於
return 123
}
fn().then(res => console.log(res)) //123
複製程式碼
async function async1 () {
console.log('async1 start');
await new Promise(resolve => {
console.log('promise1')
})
console.log('async1 success');
return 'async1 end'
}
console.log('srcipt start')
async1().then(res => console.log(res))
console.log('srcipt end')
在async1中await後面的Promise是沒有返回值的,也就是它的狀態始終是pending狀態,
因此相當於一直在await,await,await卻始終沒有響應...,
所以在await之後的內容是不會執行的,也包括async1後面的 .then。
'script start'
'async1 start'
'promise1'
'script end'
複製程式碼
注意
async function async1 () {
await new Promise(resolve => {
console.log('promise1')
resolve('promise resolve')
})
console.log('async1 success');
return 'async1 end'
}
console.log('srcipt start')
async1().then(res => {
console.log(res)
})
這道過有一點需要注意的,在async1中的newPromise它的resovle的值和async1().then()裡的值是沒有關係的,小夥伴可能看到resovle('promise resolve')就會誤以為是async1().then()中的返回值。
'script start'
'promise1'
'async1 success'
'async1 end' //此處是返回值,不是resovle('promise resolve')
複製程式碼
await 的異常處理
如果在async函式中丟擲了錯誤,則終止錯誤結果,不會繼續向下執行。
以下例子使用reject和Error,都中斷執行
async function async1 () {
await async2();
console.log('async1');
return 'async1 success'
}
async function async2 () {
return new Promise((resolve, reject) => {
console.log('async2')
reject('error')
})
}
async1().then(res => console.log(res))
'async2'
Uncaught (in promise) error
複製程式碼
改為throw new Error
async function async1 () {
console.log('async1');
throw new Error('error!!!')
return 'async1 success'
}
async1().then(res => console.log(res))
複製程式碼
如果想要使得錯誤的地方不影響async函式後續的執行的話:
可以使用try catch
- 可以直接在Promise.reject後面跟著一個catch()方法:
async function async1 () {
// try {
// await Promise.reject('error!!!')
// } catch(e) {
// console.log(e)
// } //此方法也行
await Promise.reject('error!!!')
.catch(e => console.log(e))
console.log('async1');
return Promise.resolve('async1 success')
}
async1().then(res => console.log(res))
console.log('script start')
'script start'
'error!!!'
'async1'
'async1 success'
此處注意catch執行在console.log('async1')之前,不像then的執行順序
複製程式碼
await用法的缺點和優點
優點:
- 同步程式碼思想
- 寫法上簡單
缺點:
- 有些執行沒必要等待
4. Promise和await async
- Promise結合await async
記住緊跟著await後面的語句相當於放在了new Promise中,下一行及之後的語句相當於放在於Promise.then中
async function async1() {
console.log("async1 start");
await async2();
console.log("async1 end");
}
async function async2() {
console.log("async2");
}
async1();
console.log('start')
複製程式碼
- Promise和await async的區別
Promise不會阻塞後面同步程式碼的執行,await會阻塞
async function async1() {
console.log("async1 start");
await async2();
console.log("async1 end");
}
async function async2() {
setTimeout(() => {
console.log('timer')
}, 0)
console.log("async2");
}
async1();
console.log("start")
執行結果
'async1 start'
'async2'
'start'
'async1 end'
'timer'
定時器始終還是最後執行的,它被放到下一條巨集任務的延遲佇列中。
複製程式碼
5. promise手寫程式碼
- v1.0 初始版本myPromise
function myPromise(contructor){
let self = this
self.status = "pending";
self.value = undefinded;
self.reason = undefinded;
function resolved(value){
if(self.status === 'pending'){
self.status = 'resolved';
self.value = value;
}
}
function rejected(){
if(self.status === 'pending'){
self.status = 'rejected';
self.reason = reason;
}
}
contructor(resolved,rejected)
}
myPromise.prototype.then = function(onFullFilled,onRejected){
let self = this;
switch(this.status){
case 'resolved':
onFullFilled(self.value)
case 'rejected':
onRejected(self.value)
}
}
複製程式碼
- v2.0 基於觀察者模式實現
用2個數組onFullfilledArray和onRejectedArray來儲存非同步的方法。在狀態發生改變時,一次遍歷執行陣列中的方法。
function myPromise(contructor){
let self = this
self.status = "pending";
self.value = undefinded;
self.reason = undefinded;
self.onFullfilledArray = [];
self.onRejectedArray = [];
function resolved(value){
if(self.status === 'pending'){
self.status = 'resolved';
self.onFullfilledArray.forEach(function(f){
f(self.value)
//如果狀態從pending變為resolved,那麼就遍歷執行裡面的非同步方法
})
}
}
function rejected(){
if(self.status === 'pending'){
self.status = 'rejected';
self.onRejectedArray.forEach(function(f){
f(self.reason)
//如果狀態從pending變為rejected,那麼就遍歷執行裡面的非同步方法
})
}
}
contructor(resolved,rejected)
}
myPromise.prototype.then = function(onFullFilled,onRejected){
let self = this;
switch(this.status){
case 'pending':
self.onFullfilledArray.push(function(){
onFullFilled(self.value)
}
self.onRejectedArray.push(function(){
onFullFilled(self.reason)
}
case 'resolved':
onFullFilled(self.value)
break;
case 'rejected':
onRejected(self.reason)
break;
default;
}
}
複製程式碼
通過兩個陣列,在狀態發生改變之後再開始執行,這樣可以處理非同步resolve無法呼叫的問題。這個版本的myPromise就能處理所有的非同步,那麼這樣做就完整了嗎?
- v3.0 then方法實現鏈式呼叫
myPromise.prototype.then = function(onFullFilled,onRejected){
let self = this;
let newPromise;
switch(this.status){
case 'pending':
<!--then返回promise-->
return newPromise = new myPromise(function(resolved,rejected){
self.onFullfilledArray.push(function(){
try{
let temple=onFullfilled(self.value); //得到resolved的值
resolve(temple)
}catch(e){
reject(e) //error catch
}
});
self.onFullfilledArray.push(function(){
try{
let temple=onRejected(self.reason); //得到rejected的值
reject(temple)
}catch(e){
reject(e) //error catch
}
});
})
case 'resolved':
return newPromise = new myPromise(function(resolve,reject){
try{
let temple=onFullfilled(self.value);
//將上次一then裡面的方法傳遞進下一個Promise的狀態
resolve(temple);
}catch(e){
reject(e);//error catch
}
})
break;
case 'rejected':
return newPromise = new myPromise(function(resolve,reject){
try{
let temple=onRejected(self.reason);
//將then裡面的方法傳遞到下一個Promise的狀態裡
resolve(temple);
}catch(e){
reject(e);
}
})
break;
default;
}
}
複製程式碼
這樣通過then方法返回一個promise就可以實現鏈式的呼叫:
p.then(function(x){
console.log(x)
})
.then(function(){
console.log("鏈式呼叫1")
})
.then(function(){
console.log("鏈式呼叫2")
})
//輸出
1
鏈式呼叫1
鏈式呼叫2
複製程式碼
這樣我們雖然實現了then函式的鏈式呼叫,但是還有一個問題,就是在Promise/A+規範中then函式裡面的onFullfilled方法和onRejected方法的返回值可以是物件,函式,甚至是另一個promise
。 疑問:以上onFullfilled方法和onRejected方法有返回值嗎?
4.v4.0 then函式中的onFullfilled和onRejected方法的返回值問題
function resolvePromise(promise,x,resolve,reject){
if(promise===x){
throw new TypeError("type error")
}
let isUsed;
if(x!==null&&(typeof x==="object"||typeof x==="function")){
try{
let then=x.then;
if(typeof then==="function"){
//是一個promise的情況
then.call(x,function(y){
if(isUsed)return;
isUsed=true;
resolvePromise(promise,y,resolve,reject);
},function(e){
if(isUsed)return;
isUsed=true;
reject(e);
})
}else{
//僅僅是一個函式或者是物件
resolve(x)
}
}catch(e){
if(isUsed)return;
isUsed=true;
reject(e);
}
}else{
//返回的基本型別,直接resolve
resolve(x)
}
}
複製程式碼
改變了resolvePromise函式之後,我們在then方法裡面的呼叫也變成了resolvePromise而不是promise。
myPromise.prototype.then=function(onFullfilled,onRejected){
let self=this;
let promise2;
switch(self.status){
case "pending":
promise2=new myPromise(function(resolve,reject){
self.onFullfilledArray.push(function(){
setTimeout(function(){
try{
let temple=onFullfilled(self.value);
resolvePromise(temple)
}catch(e){
reject(e) //error catch
}
})
});
self.onRejectedArray.push(function(){
setTimeout(function(){
try{
let temple=onRejected(self.reason);
resolvePromise(temple)
}catch(e){
reject(e)// error catch
}
})
});
})
case "resolved":
promise2=new myPromise(function(resolve,reject){
setTimeout(function(){
try{
let temple=onFullfilled(self.value);
//將上次一then裡面的方法傳遞進下一個Promise狀態
resolvePromise(temple);
}catch(e){
reject(e);//error catch
}
})
})
break;
case "rejected":
promise2=new myPromise(function(resolve,reject){
setTimeout(function(){
try{
let temple=onRejected(self.reason);
//將then裡面的方法傳遞到下一個Promise的狀態裡
resolvePromise(temple);
}catch(e){
reject(e);
}
})
})
break;
default:
}
return promise2;
}
複製程式碼
6. 如何讓非同步順序執行
var arr = [1, 2, 3, 4]
var promises = []
arr.map(async (value) => {
promises.push(new Promise((res) => {
setTimeout(() => {
console.log(value)
res()
}, 1000)
}))
})
var promise = Promise.resolve()
for (var i = 0; i < promises.length; i += 1) {
const task = promises[i];
promise.then(() => {
return task
})
}
複製程式碼
arr.reduce((p, x) => p.then(() => new Promise(r => setTimeout(() => r(console.log(x)), 1000))), Promise.resolve())
複製程式碼
const arr = [1, 2, 3]
arr.reduce((p, x) => {
return p.then(() => {
return new Promise(r => {
setTimeout(() => r(console.log(x)), 1000)
})
})
}, Promise.resolve())
複製程式碼
注意掌握reduce的高階用法,此處我還得多寫幾個例子加深理解
7. 場景題目
使用Promise實現每隔1秒輸出1,2,3
做此題先寫迴圈方法程式碼,不能滿足要求
function print(){
for (let i=1;i<4;i++){
console.log('first:',i)
new Promise(()=>{
console.log('second:',i)
setTimeout(function(){
console.log(i++)
},1000)
})
}
}
print()
async function print(){
let a = [1,2,3]
for (let i of a){
console.log('first',i)
await new Promise((resolved)=>{
resolved(i)
}).then(()=>{
console.log('second:',i)
setTimeout(function(){
console.log(i)
},1000)})
}
}
print()
//都是先輸出
> "first" 1
> "second:" 1
> "first" 2
> "second:" 2
> "first" 3
> "second:" 3
再1s後同時並行輸出
> 1
> 2
> 3
原因:在一個迴圈作用域下,執行非同步
new Promise是微任務,setTimeout是巨集任務
主程式完成之後,執行微任務,巨集任務壓入執行佇列中
複製程式碼
async function print(){
let a = [1,2,3]
for (let i of a){
console.log('first',i)
await new Promise((resolved)=>{
resolved(i)
}).then(()=>{
console.log('second:',i)
// setTimeout(function(){
console.log(i)
// },1000)
})
}
}
> "first" 1
> "second:" 1
> 1
> "first" 2
> "second:" 2
> 2
> "first" 3
> "second:" 3
> 3
複製程式碼
function print(i){
return new Promise((resolved)=>{
setTimeout(function(){
console.log(i)
resolved(i)
},2000)
})
}
print(1).then(()=>{
print(2)
}).then(()=>{
print(3)
})
2s後先出現1,再出現2,3,因為setTimeout最後面執行
> 1
> 2
> 3
複製程式碼
function print(i){
return new Promise((resolved)=>{
setTimeout(function(){
console.log(i)
resolved(i)
},2000)
})
}
async function a(i){
await print(i)
}
a(1)
a(2)
a(3)
2s後同時出現
> 1
> 2
> 3
複製程式碼
var arr = [1, 2, 3, 4]
var promises = []
arr.map(async (value) => {
promises.push(new Promise((res) => {
setTimeout(() => {
console.log(value)
res()
}, 2000)
}))
})
var promise = Promise.resolve()
for (var i = 0; i < promises.length; i += 1) {
const task = promises[i]
promise
.then(() => {
return task
})
}
2s後同時出現
> 1
> 2
> 3
> 4
複製程式碼
把let改成var
function print(){
for (var i=1;i<4;i++){
console.log('first:',i)
new Promise(()=>{
console.log('second:',i)
setTimeout(function(){
console.log(i++)
},1000)
})
}
}
print()
//先輸出
> "first" 1
> "second:" 1
> "first" 2
> "second:" 2
> "first" 3
> "second:" 3
再1s後同時並行輸出
> 4
> 5
> 6
因為先執行了for迴圈,再執行new Promise
當非同步事件發生時,會建立事件並放入執行佇列中,等待當前程式碼執行完成之後再執行這些程式碼。
複製程式碼
如何讓非同步操作順序執行 最終程式碼:
const arr = [1, 2, 3]
arr.reduce((p, x) => {
return p.then(() => {
return new Promise(r => {
setTimeout(() => r(console.log(x)), 1000)
})
})
}, Promise.resolve())
部分變成非箭頭函式
const arr = [1, 2, 3]
arr.reduce((p, x) => {
return p.then(() => {
return new Promise(function(resolved){
setTimeout(function(){
resolved(console.log(x))
//console.log(x) 如果沒有resolved()包含console.log(x),則僅僅1s後輸出1
}, 1000)
})
})
}, Promise.resolve())
要知道resolved是個函式
每隔1s後輸出
> 1
> 2
> 3
複製程式碼
紅燈3秒亮一次,黃燈2秒亮一次,綠燈1秒亮一次,使用Promise實現紅綠燈交替重複亮?
function red() {
console.log('red');
}
function green() {
console.log('green');
}
function yellow() {
console.log('yellow');
}
new Promise((resolve)=>{
setTimeout(()=>{
red()
resolve()
},3000)
}).then((resolve) => {
setTimeout(()=>{
green()
//resolve()
},2000)
}).then((resolve) => {
setTimeout(()=>{
yellow()
// resolve()
},1000)
})
> "red"
> "yellow"
> "green"
報錯:VM5613:22 Uncaught TypeError: resolve is not a function
要知道then後面沒有resolved函式
複製程式碼
下面換return方式試:
function print(timer,cb){
return new Promise(function(resolve){
setTimeout(() => {
cb()
resolve()
}, timer)
})
}
Promise.resolve().then(() => {
return print(3000, red)
}).then(() => {
return print(2000, green)
}).then(() => {
return print(1000, yellow)
})
3s後輸出"red",2s後輸出"green",1s後輸出"yellow"
> "red"
> "green"
> "yellow"
我自己的解釋:Promise.resolve方法允許呼叫時不帶引數,直接返回一個resolved狀態的 Promise 物件,所以回撥函式會立即執行;then()函式裡不返回值或者返回的不是promise,那麼 then 返回的 Promise 將會成為接受狀態(resolve)
`都是在then之後返回print,也就是new Promise`
new Promise(()=>{
return print(3000, red)
}).then(() => {
return print(2000, green)
}).then(() => {
return print(1000, yellow)
})
3s後輸出
> "red"
new Promise((resolve)=>{
//return new Promise(function(resolve){
setTimeout(() => {
red()
resolve()
}, 3000)
//})
}).then(() => {
return new Promise(function(resolve){
setTimeout(() => {
green()
resolve()
}, 2000)
})
}).then(() => {
return new Promise(function(resolve){
setTimeout(() => {
yellow()
resolve()
}, 2000)
})
})
註釋then之前的return,為了獲取resolve狀態,這個很能對比Promise.resolve()
> 3s後輸出"red",2s後輸出"green",1s後輸出"yellow"
複製程式碼
這個想象讓我問自己為什麼第一行必須寫Promise.resolve()
來了解下new Promise()和Promise.resolve()的區別
Promise.resolve()
1)如果引數是一個Promise例項,Promise.resolve將不做任何修改、原封不動地返回這個例項
let v = new Promise(resolve => {
console.log("begin");
//resolve("then");
});
Promise.resolve(v)
> "begin"
let v = new Promise(resolve => {
console.log("begin");
resolve("then");
});
Promise.resolve(v).then((res)=>console.log(res))
> "begin"
> "then"
複製程式碼
2)如果引數是一個包含then的物件
Promise.resolve方法會將這個物件轉為 Promise 物件例項
let thenable = {
then: function(resolve, reject) {
resolve(42);
}
}
let p1 = Promise.resolve(thenable);
p1.then(function(value) {
console.log(value); // 42
});
複製程式碼
3)引數不是具有then方法的物件,或根本就不是物件,也不是Promise例項
Promise.resolve方法返回一個新的 Promise 物件,狀態為resolved
const p = Promise.resolve('HHY');
console.log(p) //Promise{<resolved>: "HHY"}
p.then(function (s){
console.log(s)
});
> HHY
複製程式碼
4)不帶任何引數
直接返回一個resolved狀態的 Promise 物件
console.log(Promise.resolve()) //Promise{<resolved>: undefined}
setTimeout(function () {
console.log('three');
}, 0);
Promise.resolve().then(function () {
console.log('two');
});
console.log('one');
> "one"
> "two"
> "three"
複製程式碼
.then()函式裡不返回值或者返回的不是promise
,那麼 then 返回的 Promise 將會成為接受狀態(resolve)
Promise.resolve()
.then(() => console.log(2))
.then(() => console.log(3)
);
console.log(1);
> 1
> 2
> 3
複製程式碼
先輸出1的原因:
Promise.resolve(),是在本輪“事件迴圈”(event loop)的結束時執行
,不是馬上執行,也不是在下一輪“事件迴圈”的開始時執行.
原因:傳遞到 then() 中的函式被置入了一個微任務佇列
,而不是立即執行,這意味著它是在 JS 事件佇列的所有執行時結束了,事件佇列(我的理解是同步程式碼的事件佇列)被清空之後
,才開始執行.
new Promise()
let v = new Promise(resolve => {
console.log("begin");
resolve("then");
});
複製程式碼
在promise裡面resolve一個狀態為fulfilled的promise
resolve()本質作用:
resolve()是用來表示promise的狀態為fullfilled,相當於只是定義了一個有狀態的Promise,但是並沒有呼叫它; promise呼叫then的前提是promise的狀態為fullfilled; 只有promise呼叫then的時候,then裡面的函式才會被推入微任務中;
區別
- new promise是返回一個promise物件
- Promise.resolve() 返回一個promise物件,狀態為resolved
- Promise.resolve方法允許呼叫時不帶引數,直接返回一個resolved狀態的 Promise 物件,所以回撥函式會立即執行;then()函式裡不返回值或者返回的不是promise,那麼 then 返回的 Promise 將會成為接受狀態(resolve)
因此new promise的操作就跟你 new 一個普通函式沒區別,所以這一句其實是巨集任務,但後面的then是微任務
resolved後的promise物件會在這該級別事件佇列結束之後才開始執行,及執行與該輪微任務佇列中,始於下一級別巨集任務之前
Promise.resolve()可以返回例項物件。
Promise.resolve(v)不等於new Promise(r => r(v))
當v是一個Promise例項的時候就會出現一些不同的地方
// v是一個例項化的promise,且狀態為fulfilled
let v = new Promise(resolve => {
console.log("begin");
resolve("then");
});
模式一 new Promise裡的resolve()
結果: begin->1->2->3->then->4 可以發現then推遲了兩個時序
推遲原因:瀏覽器會建立一個 PromiseResolveThenableJob 去處理這個 Promise 例項,這是一個微任務。
等到下次迴圈到來這個微任務會執行,也就是PromiseResolveThenableJob 執行中的時候,因為這個Promise 例項是fulfilled狀態,所以又會註冊一個它的.then()回撥
又等一次迴圈到這個Promise 例項它的.then()回撥執行後,
才會註冊下面的這個.then(),於是就被推遲了兩個時序
new Promise(resolve => {
console.log(v) //Promise{<resolved>: "then"}
console.log(resolve(v)) //undefined
resolve(v);
}).then((v)=>{
console.log(v) //獲取then值得地方
}); //begin->1->2->3->then->4
我的理解:resolve中包含例項物件,先輸出begin,再執行下面輸出1,2,3(1理解,為什麼會執行了2和3,再then呢?),再來執行獲取then值得地方
模式二 Promise.resolve(v)直接建立
結果:begin->1->then->2->3->4 可以發現then的執行時間正常了,第一個執行的微任務就是下面這個.then
原因:Promise.resolve()API如果引數是promise會直接返回這個promise例項,不會做任何處理
Promise.resolve(v).then((v)=>{
console.log(v)
}); //begin->1->then->2->3->4
new Promise(resolve => {
console.log(1);
resolve();
})
.then(() => {
console.log(2);
})
.then(() => {
console.log(3);
})
.then(() => {
console.log(4);
});
> "begin"
> 1
> "then"
> 2
> 3
> 4
> "begin"
> 1
> 2
> 3
> "then"
> 4
複製程式碼
實現mergePromise函式
把傳進去的陣列按順序先後執行,並且把返回的資料先後放到陣列data中。
const time = (timer) => {
return new Promise(resolve => {
setTimeout(() => {
resolve()
}, timer)
})
}
const ajax1 = () => time(2000).then(() => {
console.log(1);
return 1
})
const ajax2 = () => time(1000).then(() => {
console.log(2);
return 2
})
const ajax3 = () => time(1000).then(() => {
console.log(3);
return 3
})
function mergePromise (ajaxArray) {
// 存放每個ajax的結果
const data = [];
let promise = Promise.resolve();
ajaxArray.forEach(ajax => {
// 第一次的then為了用來呼叫ajax
// 第二次的then是為了獲取ajax的結果
promise = promise.then(ajax).then(res => {
data.push(res);
return data; // 把每次的結果返回
})
})
// 最後得到的promise它的值就是data
return promise;
}
mergePromise([ajax1, ajax2, ajax3]).then(data => {
console.log("done");
console.log(data); // data 為 [1, 2, 3]
});
複製程式碼
根據promiseA+實現一個自己的promise
封裝一個非同步載入圖片的方法
,只需要在圖片的onload函式中,使用resolve返回一下就可以了。
function loadImg(url) {
return new Promise((resolve, reject) => {
const img = new Image();
img.onload = function() {
console.log("一張圖片載入完成");
resolve(img);
};
img.onerror = function() {
reject(new Error('Could not load image at' + url));
};
img.src = url;
});
複製程式碼
限制非同步操作的併發個數並儘可能快的完成全部
8. 參考連結
9. 後語
花了一週時間整理,一些標記是自己不熟悉的地方,另外一些昇華題需要繼續研究,有時間繼續補充和更正,請大家閱讀,有收穫請給個