then()方法的作用是Promise實例添加解決(fulfillment)和拒絕(rejection)狀態的回調函數。
then()方法會返回一個新的Promise實例,所以then()方法后面可以繼續跟另一個then()方法進行鏈式調用。
let p = new Promise((resolve, reject) => { setTimeout(resolve, 1000, 'success'); }); p.then( res => { console.log(res); return `${res} again`; } ) .then( res => console.log(res) ); // 連續 // success // success again
但是前一個then()方法中的回調函數中又可能返回一個Promise實例,這時候后面一個then()方法中的回調函數會等前一個Promise實例的狀態發生變化才會調用。
參考來源 :https://cloud.tencent.com/developer/article/1405717
let p = new Promise((resolve, reject) => { setTimeout(resolve, 1000, 'success'); }); p.then( res => { console.log(res); return new Promise((resolve, reject) => { setTimeout(resolve, 1000, 'success'); }); } ) .then( res => console.log(res) ); // 相隔1000ms // success // success
補充:settimeout(fn 0)與Promise的執行順序
示例如下
setTimeout(function() { console.log(1) }, 0); new Promise(function(resolve, reject) { console.log(2) for (var i = 0; i < 10000; i++) { if(i === 10) {console.log(10)} i == 9999 && resolve(); } console.log(3) }).then(function() { console.log(4) }) console.log(5);
結果:2 10 3 5 4 1
解析:
1. setTimeout(fn, 0)何時執行?
我們知道,JavaScript是基於事件驅動單線程執行的,所有任務都需要排隊,也就是說前一個任務結束,才會去執行下一個任務。而像settimeout、ajax等異步操作的回調,會進入”任務隊列“中,而且只有主線程中沒有執行任何同步代碼的前提下,才會執行異步回調。
而settimeout(fn, 0)表示立即執行,也就是用來改變任務的執行順序,要求瀏覽器”盡可能快“的進行回調。
2. promise何時執行?
我們依舊以上面代碼為例:
new Promise(function(resolve, reject) { console.log(2) for (var i = 0; i < 10000; i++) { if(i === 10) {console.log(10)} i == 9999 && resolve(); } console.log(3) })
結果: 2 10 3
從結果可以看出,Promise
新建后立即執行,也就是說,Promise構造函數
里的代碼是同步執行的。
3. then何時執行?
看看實例:
new Promise(function(resolve, reject) { console.log(2) for (var i = 0; i < 10000; i++) { if(i === 10) {console.log(10)} i == 9999 && resolve(); } console.log(3) }).then(function() { console.log(4) }) for (var i = 0; i < 5; i++) { console.log('a' + i); }
結果: 2 10 3 a0 a1 a2 a3 a4 4
從結果來看,可以知道then
方法指向的回調將在當前腳本所有同步任務執行完后執行。
再來回顧第一個例子:
setTimeout(function() { console.log(1) }, 0); new Promise(function(resolve, reject) { console.log(2) for (var i = 0; i < 10000; i++) { if(i === 10) {console.log(10)} i == 9999 && resolve(); } console.log(3) }).then(function() { console.log(4) }) console.log(5);
結果是:2 10 3 5 4 1
通過上面的實例,相信你已經解開了三個“何時
”。可能你還有一個疑惑,那就是為什么then
比setTimeout
執行的要早呢?
目前有兩種原因導致:
1) setTimeout的0是否真的為0?
其實,setTimeout有個最小執行時間
(minimum delay of 4ms
),並不是0s
執行的。
注:HTML5中已經將最小執行時間統一為4ms。
2) macrotask 與 microtask
Macrotasks和Microtasks 都屬於異步任務中的一種,常用api分類: macrotasks: setTimeout, setInterval, setImmediate, I/O, UI rendering microtasks: process.nextTick, Promise, MutationObserver
一個事件循環中只有一個macrotask任務,可以有一個或多個microtask任務。
來看看上面實例的執行:
- 首先,setTimeout 被推進到 macrotask 隊列(將在下一個macrotask中執行)中。
- 接着, 會先執行 macrotask 中的第一個任務(整個 script中的同步代碼 ),再加上promise 構造函數也是同步的(promise.then 回調被推進到 microtask 隊列中),所以會先打印出2 10 3,然后繼續執行末尾的,打印出5
- 此時,已經執行完了第一個 macrotask , 所以接下來會順序執行所有的 microtask, 也就是 promise.then 的回調函數,從而打印出4。
- 此時,microtask 隊列中的任務已經執行完畢,所以執行剩下的 macrotask 隊列中的任務,也就是 setTimeout, 所以打印出 1.
經過層層測試,所以最終得出的結論是: 同步代碼(包括promise的構造函數) -> promise.then -> setTimeout
部分參考來源:https://cloud.tencent.com/developer/article/1405717