作者:艾特老干部
連接:https://juejin.im/post/6844903488695042062
來源:掘金
let foo = new Promise((resolve, reject) => {
console.log('我會先執行');
resolve('成功')
})
console.log('在executor之後');
foo.then(data => {
console.log(data);
})
Promsie的executor是同步的
let foo = new Promise( (resolve, reject) => {...} // executor (可以放異步代碼) )
var p1 = new Promise(function (resolve, reject) {
resolve(1);
});
var p2 = new Promise(function (resolve, reject) {
setTimeout(function () {
console.log('PromiseP2 Executor')
resolve(2);
}, 500);
});
var p3 = new Promise(function (resolve, reject) {
setTimeout(function () {
console.log('PromiseP3 Executor')
reject(3);
}, 500);
});
console.log(p1);
console.log(p2);
console.log(p3);
setTimeout(function () {
console.log('p2 setTimeout')
console.log(p2);
}, 1000);
setTimeout(function () {
console.log('p3 setTimeout')
console.log(p3);
}, 1000);
p1.then(function (value) {
console.log('p1 then')
console.log(value);
});
Promise.resolve().then(data => {
console.log('haha then');
})
p2.then(function (value) {
console.log(value);
});
p3.catch(function (err) {
console.log(err);
});
第一階段
立即執行p1的resolve改變了p1的狀態
將 PromiseP2 Executor 以及 PromiseP3 Executor 依序放到宏任務隊列裡
接下來依序打印p1、p2、p3
附註: 此時p1已經轉換狀態(立即執行了resolve,因此狀態變成resolved)
接下來將 p2 setTimeout、p3 setTimeout 放到宏任務
注意這個想法很關鍵,因為then需要偵測到狀態已經改變才會被添加進微任務對列裡面,所以此時只會p1 then 以及haha then會被放到微任務隊列裡
此時的宏任務與微任務:
第二階段
執行p1 then,以及haha then(這裡當然還要注意p1 then裡面有沒有做甚麼其他事,不過此題很明顯沒有所以才直接執行),打印了p1 then、1、haha then
微任務全部執行完畢,接下來先執行Promise2 Executor,打印出PromiseP2 Executor,並resolve(2),此時因為狀態改變,p2的then因此被放進微任務隊列裡
注意:每執行完一個宏任務,瀏覽器就會檢查一次微任務隊列是否為空
此時的宏任務與微任務:
第三階段
- 執行p2 then ,打印出2,接下來照剛剛的邏輯跑一次
- 因此先打印出PromiseP3 Executor,再打印出3
- 最後剩下兩個宏任務,因此打印出剩下的全部(注意,每完成一次宏任務就要檢查微任務是否為空,步過這兩個也沒做甚麼特別的事,因此這邊不再贅述)
總結:
這裡主要是想表達Promise實例經過resolve()與reject()回調後會讓狀態改變(但我覺得這是一題很好的EventLoop題目)
附註: 以上皆是針對瀏覽器的EventLoop
let foo = new Promise((resolve, reject) => {
resolve(5)
reject('失敗')
})
console.log(foo);
foo.then(data => {
console.log(data);
})
雖然狀態改變後(resolve) 試圖透過reject再改變他的狀態,但是我們發現打印出來還是resolved
let foo = new Promise(resolve => {
resolve(1)
// 第一個then
}).then(data => {
console.log(data); // 1
return data * 2
// 第二個then
}).then(data => {
console.log(data); // 2
// 第三個then
}).then(data => {
console.log(data); // 因為上一個then沒有返回東西所以是undefined
return Promise.resolve('resolve');
// 第四個then
}).then(data => {
console.log(data); // 上一個返回Promise對象因此輸出resolve
return Promise.reject('reject');
// 第五個then
}).then(data => {
console.log(data);
}, err =>{
console.log(err); // reject
// 第六個then
}).then(data => {
console.log(data, 123456);
})
then方法會返回一個新的Promise對象,因此可以用鍊式調用(then後面可以接then)
共有兩個參數,第一個是成功的回調函數,第二個是失敗,只有一個會被調用
回調函數(不管是then的哪個參數)的返回值有下列幾種可能
返回一個同步的值,或者
undefined
(當沒有返回一個有效值時,默認返回undefined),then方法將返回一個resolved的Promise對象,對象的value即是返回值
return
另一個Promise,then
方法將根據這個Promise的狀態和值創建一個新的Promise對象返回。
之前講過(微任務)
var p1 = new Promise(function (resolve, reject) {
foo.bar(); // 根本沒有這種方法
resolve(1);
});
p1.then(
function (value) {
console.log('p1 then value: ' + value);
},
function (err) {
console.log('p1 then err: ' + err);
}
).then(
function (value) {
console.log('p1 then then value: ' + value);
},
function (err) {
console.log('p1 then then err: ' + err);
}
);
var p2 = new Promise(function (resolve, reject) {
resolve(2);
});
p2.then(
function (value) {
console.log('p2 then value: ' + value);
foo.bar();
},
function (err) {
console.log('p2 then err: ' + err);
}
).then(
function (value) {
console.log('p2 then then value: ' + value);
},
function (err) {
console.log('p2 then then err: ' + err);
return 1;
}
).then(
function (value) {
console.log('p2 then then then value: ' + value);
},
function (err) {
console.log('p2 then then then err: ' + err);
}
);
這裡我覺得EventLoop講的已經夠多了,所以我不打算在這邊贅述執行的順序是怎樣,我只針對他們輸出的值分析
第一部分
var p1 = new Promise(function (resolve, reject) {
foo.bar(); // 根本沒有這種方法,Promise會返回一個錯誤對象
resolve(1);
// 注意如果順序有變會改變結果,因為Promise狀態一旦改變就不會再變
// 所以代碼如果為以下
// resolve(1)
// foo.bar()
// 則底下then會偵測到狀態改變執行第一個回調函數
});
p1.then(
function (value) {
console.log('p1 then value: ' + value);
},
function (err) { // 因為接收到rejected狀態,所以then執行第二個參數
console.log('p1 then err: ' + err);
}
).then(
function (value) { // 因為上一個then執行的回調函數( function(err){...} )沒有返回值,因此默認返回resolved的Promise對象,value為undefined
console.log('p1 then then value: ' + value);
},
function (err) {
console.log('p1 then then err: ' + err);
}
);
第二部分
var p2 = new Promise(function (resolve, reject) {
resolve(2);
});
p2.then(
function (value) { // 合理執行第一個參數,value為2
console.log('p2 then value: ' + value);
foo.bar(); // 這裡報錯,所以Promise對象狀態改為rejected
},
function (err) {
console.log('p2 then err: ' + err);
}
).then(
function (value) {
console.log('p2 then then value: ' + value);
},
function (err) {
console.log('p2 then then err: ' + err); // 偵測到Promise對象轉成rejected,將報錯傳進來
return 1; // 返回1,因此返回Promise對象為resolve,value為1
}
).then(
function (value) {
console.log('p2 then then then value: ' + value); // 執行這一行
},
function (err) {
console.log('p2 then then then err: ' + err);
}
);
var p1 = Promise.resolve(1);
var p2 = Promise.resolve(p1);
var p3 = new Promise(function (resolve, reject) {
resolve(1);
});
var p4 = new Promise(function (resolve, reject) {
resolve(p1);
});
console.log(p1 === p2);
console.log(p1 === p3);
console.log(p1 === p4);
console.log(p3 === p4);
p4.then(function (value) {
console.log('p4=' + value);
});
p2.then(function (value) {
console.log('p2=' + value);
})
p1.then(function (value) {
console.log('p1=' + value);
})
Promise.resolve(...)
可以接收一個值或者是一個Promise對像作為參數
- 參數是普通值,返回一個resolved狀態的Promise對象,對象的value就是這個參數
- 參數是一個Promise對象,則直接返回這個Promise參數。
第一個part
var p1 = Promise.resolve(1);
var p2 = Promise.resolve(p1); // 因為resolve(...) 假如傳入的是Promise對象直接return該對象
// 所以上面這行等於
// var p2 = Promise.resolve(1);
var p3 = new Promise(function (resolve, reject) {
resolve(1);
});
var p4 = new Promise(function (resolve, reject) {
resolve(p1);
});
console.log(p1 === p2);
// 底下全為false非常合理,因為都是p3、p4都是創建新的實例
console.log(p1 === p3);
console.log(p1 === p4);
console.log(p3 === p4);
第二個part
var p1 = Promise.resolve(1);
var p2 = Promise.resolve(p1);
var p4 = new Promise(function (resolve, reject) {
resolve(p1);
});
p4.then(function (value) { // 要先等待resolve改變完成才會放到微任務隊列(所以會比p1、p2晚放進隊列)
console.log('p4=' + value);
});
p2.then(function (value) {
console.log('p2=' + value);
})
p1.then(function (value) {
console.log('p1=' + value);
})
這裡要探討的問題是:為啥p4明明先執行為啥在最後
主要是因為
var p4 = new Promise(function (resolve, reject) { resolve(p1); // resolve會對p1”拆箱“,獲取p1的狀態和值,但這個過程是異步的 });
var p1 = new Promise(function (resolve, reject) {
resolve(Promise.resolve('resolve')); // 返回Promise.resolve('resolve') (異步)
});
var p2 = new Promise(function (resolve, reject) {
resolve(Promise.reject('reject')); // 返回Promise.reject('reject') (異步)
});
var p3 = new Promise(function (resolve, reject) {
reject(Promise.resolve('resolve')); // 非異步,直接返回一個rejected的Promise對象,value為裡面那個resolved的Promise對象(也因為非異步因此會讓p3 then比p1、p2更早進入微任務隊列)
});
p1.then(
function fulfilled(value) {
console.log('fulfilled1: ' + value);
},
function rejected(err) {
console.log('rejected1: ' + err);
}
);
p2.then(
function fulfilled(value) {
console.log('fulfilled2: ' + value);
},
function rejected(err) {
console.log('rejected2: ' + err);
}
);
p3.then(
function fulfilled(value) {
console.log('fulfilled3: ' + value);
},
function rejected(err) {
console.log('rejected3: ' + err);
// 我這裡將作者的代碼新增了下面這行
console.log(err)
}
);