ES6 Promise對象then方法鏈式調用及和settimeout(fn 0)執行順序


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

通過上面的實例,相信你已經解開了三個“何時”。可能你還有一個疑惑,那就是為什么thensetTimeout執行的要早呢?

目前有兩種原因導致:

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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM