本文首發於 vivo互聯網技術 微信公眾號
鏈接: https://mp.weixin.qq.com/s/Xz2bGaLxVL4xw1M2hb2nJQ
作者:Morrain
很多同學在學習 Promise 時,知其然卻不知其所以然,對其中的用法理解不了。本系列文章由淺入深逐步實現 Promise,並結合流程圖、實例以及動畫進行演示,達到深刻理解 Promise 用法的目的。
本系列文章有如下幾個章節組成:
- 圖解 Promise 實現原理(一)—— 基礎實現
- 圖解 Promise 實現原理(二)—— Promise 鏈式調用
- 圖解 Promise 實現原理(三)—— Promise 原型方法實現
- 圖解 Promise 實現原理(四)—— Promise 靜態方法實現
一、前言
上一節中,實現了 Promise 的基礎版本:
但鏈式調用,只是在 then 方法中 return 了 this,使得 Promise 實例可以多次調用 then 方法,但因為是同一個實例,調用再多次 then 也只能返回相同的一個結果,通常我們希望的鏈式調用是這樣的:
每個 then 注冊的 onFulfilled 都返回了不同的結果,層層遞進,很明顯在 then 方法中 return this 不能達到這個效果。引入真正的鏈式調用,then 返回的一定是一個新的Promise實例。

真正的鏈式 Promise 是指在當前 Promise 達到 fulfilled 狀態后,即開始進行下一個 Promise(后鄰 Promise)。那么我們如何銜接當前 Promise 和后鄰 Promise 呢?(這是理解 Promise 的難點,我們會通過動畫演示這個過程)。
二、鏈式調用的實現
先看下實現源碼:
由上面的實現,我們可以看到:
- then 方法中,創建並返回了新的 Promise 實例,這是串行Promise的基礎,是實現真正鏈式調用的根本。
- then 方法傳入的形參 onFulfilled 以及創建新 Promise 實例時傳入的 resolve 放在一起,被push到當前 Promise 的 callbacks 隊列中,這是銜接當前 Promise 和后鄰 Promise 的關鍵所在。
- 根據規范,onFulfilled 是可以為空的,為空時不調用 onFulfilled。
看下動畫演示:

(Promise 鏈式調用演示動畫)
當第一個 Promise 成功時,resolve 方法將其狀態置為 fulfilled ,並保存 resolve 帶過來的value。然后取出 callbacks 中的對象,執行當前 Promise的 onFulfilled,返回值通過調用第二個 Promise 的 resolve 方法,傳遞給第二個 Promise。動畫演示如下:

(Promise 鏈式調用 fulfilled)
為了真實的看到鏈式調用的過程,我寫一個mockAjax函數,用來模擬異步請求:
除此之外,我給 Promise 的源碼加上了日志輸出並增加了構造順序標識,可以清楚的看到構造以及執行過程:
執行結果如下:
通過打印出來的日志,可以看到:
-
構造 Promise-1 實例,立即執行 mackAjax('getUserId',callback);
-
調用 Promise-1 的 then 方法,注冊 Promise-1 的 onFulfilled 函數。
-
then 函數內部構造了一個新的 Promise實例:Promise-2。立即執行 Promise-1 的 _handle方法。
-
此時 Promise-1 還是pending的狀態。
-
Promise-1._handle 中就把注冊在 Promise-1 的 onFulfilled 和 Promise-2 的 resolve 保存在 Promise-1 內部的 callbacks。
-
至此當前線程執行結束。返回的是 Promise-2 的 Promise實例。
-
1s后,異步請求返回,要改變 Promise-1 的狀態和結果,執行 resolve(result)。
-
Promise-1 的值被改變,內容為異步請求返回的結果:"getUserId異步請求耗時1s"。
-
Promise-1 的狀態變成 fulfilled。
-
Promise-1 的 onFulfilled 被執行,打印出了"getUserId異步請求耗時1秒"。
-
然后再調用 Promise-2.resolve。
-
改變 Promise-2 的值和狀態,因為 Promise-1 的 onFulfilled 沒有返回值,所以 Promise-2的值為undefined。
上例中,如果把異步的請求改成同步會是什么的效果?
感興趣的可以自己去分析一下。
三、鏈式調用真正的意義
執行當前 Promise 的 onFulfilled 時,返回值通過調用第二個 Promise 的 resolve 方法,傳遞給第二個 Promise,作為第二個 Promise 的值。於是我們考慮如下Demo:
我們加了一層 then,來看下執行的結果:
鏈式調用可以無限的寫下去,上一級 onFulfilled return 的值,會變成下一級 onFulfilled 的結果。可以參考Demo3:
我們很容易發現,上述 Demo3 中只有第一個是異步請求,后面都是同步的,我們完全沒有必要這么鏈式的實現。如下一樣能得到我們想要的三個結果: 分別打印出來的值。
那鏈式調用真正的意義在哪里呢?
剛才演示的都是 onFulfilled 返回值是 value 的情況,如果是一個 Promise 呢?是不是就可以通過 onFulfilled,由使用 Promise 的開發者決定后續 Promise 的狀態。
於是在 _resolve 中增加對前一個 Promise onFulfilled 返回值的判斷:
從代碼上看,它是對 resolve 中的值作了一個特殊的判斷,判斷 resolve 的值是否為 Promise實例,如果是 Promise 實例,那么就把當前 Promise 實例的狀態改變接口重新注冊到 resolve 的值對應的 Promise 的 onFulfilled 中,也就是說當前 Promise 實例的狀態要依賴 resolve 的值的 Promise 實例的狀態。

執行的結果如下:
一樣的,我做了一個演示動畫,還原了這個過程:

(Promise 真正的鏈式調用)
至此,就實現了 Promise 鏈式調用的全部內容。鏈式調用是 Promise 難點,更是重點。一定要通過實例還有動畫,深刻體會。下一節介紹 Promise 其它原型方法的實現。
更多內容敬請關注 vivo 互聯網技術 微信公眾號

注:轉載文章請先與微信號:Labs2020 聯系。
