天天看點

promise的狀态以及api介紹_如何實作一個 promise

本文轉載于 SegmentFault 社群

社群專欄:chlorine

作者:chlorine

promise的狀态以及api介紹_如何實作一個 promise

promise 是 ES6 中新增的一種異步解決方案,在日常開發中也經常能看見它的身影,例如原生的 fetch API 就是基于 promise 實作的。那麼 promise 有哪些特性,如何實作一個具有 promise/A+ 規範的 promise 呢?

promise 特性

首先我們整理一下 promise 的一些基本特性和 API,完整的 promise/A+ 規範可以參考【翻譯】Promises/A+規範

•  狀态機

  • 具有 pending、fulfilled、rejected 三個狀态
  • 隻能由 pending -> fulfilled 和 pending -> rejected 這兩種狀态變化,且一經改變之後狀态不可再變
  • 成功時必須有一個不可改變的值 value,失敗時必須有一個不可改變的拒因 reason

•  構造函數

  • Promise 接受一個函數作為參數,函數擁有兩個參數 fulfill 和 reject
  • fulfill 将 promise 狀态從 pending 置為 fulfilled,傳回操作的結果
  • reject 将 promise 狀态從 pending 置為 rejected,傳回産生的錯誤

•  then 方法

  • 接受兩個參數 onFulfilled 和 onRejected,分别表示 promise 成功和失敗的回調
  • 傳回值會作為參數傳遞到下一個 then 方法的參數中

•  異步處理

•  鍊式調用

•  其他 API

  • catch、finally
  • resolve、reject、race、all 等

實作

接下來我們逐漸實作一個具有 promise/A+ 規範的 promise

基本實作

先定義一個常量,表示 promise 的三個狀态  

然後在 promise 中初始化兩個參數 value 和 reason ,分别表示狀态為 fulfill 和 reject 時的值,接着定義兩個函數,函數内部更新狀态以及相應的字段值,分别在成功和失敗的時候執行,然後将這兩個函數傳入構造函數的函數參數中,如下:

接下來初步實作一個 then 方法,當目前狀态是 fulfulled 時,執行成功回調,目前狀态為 rejected 時,執行失敗回調:  

這個時候一個簡單的 MyPromise 就實作了,但是此時它還隻能處理同步任務,對于異步操作卻無能為力。

異步處理

要想處理異步操作,可以利用隊列的特性,将回調函數先緩存起來,等到異步操作的結果傳回之後,再去執行相應的回調函數。 具體實作來看,在 then 方法中增加判斷,若為 pending 狀态,将傳入的函數寫入對應的回調函數隊列;在初始化 promise 時利用兩個數組分别儲存成功和失敗的回調函數隊列,并在 fulfill 和 reject 回調中增加它們。如下:

鍊式調用

接下來對 MyPromise 進行進一步改造,使其能夠支援鍊式調用,使用過 jquery 等庫應該對于鍊式調用非常熟悉,它的原理就是調用者傳回它本身,在這裡的話就是要讓 then 方法傳回一個 promise 即可,還有一點就是對于傳回值的傳遞: 實作到這一步的 MyPromise 已經可以支援異步操作、鍊式調用、傳遞傳回值,算是一個簡易版的 promise ,一般來說面試時需要手寫一個 promise 時,到這個程度就足夠了,完整實作 promise/A+ 規範在面試這樣一個較短的時間内也不太現實。

到這一步的完整代碼可以參考  promise3.js: https://github.com/lvqq/Demos/blob/master/promise/src/promise3.js

promise/A+ 規範

promise/A+ 規範中規定, onFulfilled/onRejected 傳回一個值 x ,對 x 需要作以下處理: •  如果 x 與 then 方法傳回的 promise 相等,抛出一個  TypeError  錯誤 •  如果 x 是一個  Promise  ,則保持 then 方法傳回的 promise 的值與 x 的值一緻 •  如果 x 是對象或函數,則将  x.then  指派給  then  并調用

  • 如果 then 是一個函數,則将 x 作為作用域 this 調用,并傳遞兩個參數 resolvePromise 和 rejectPromise,如果 resolvePromise 和 rejectPromise 均被調用或者被調用多次,則采用首次調用并忽略剩餘調用
  • 如果調用 then 方法出錯,則以抛出的錯誤 e 為拒因拒絕 promise
  • 如果 then 不是函數,則以 x 為參數執行 promise

•  如果 x 是其他值,則以 x 為參數執行 promise 接下來對上一步實作的 MyPromise 進行進一步優化,使其符合 promise/A+ 規範:  

這裡将處理傳回值 x 的行為封裝成為了一個函數 generatePromise ,實作如下:

promise/A+ 規範中還規定,對于  promise2 = promise1.then(onFulfilled, onRejected) •  onFulfilled/onRejected 必須異步調用,不能同步 •  如果 onFulfilled 不是函數且 promise1 成功執行, promise2 必須成功執行并傳回相同的值 •  如果 onRejected 不是函數且 promise1 拒絕執行, promise2 必須拒絕執行并傳回相同的拒因 對于 then 方法做最後的完善,增加 setTimeout 模拟異步調用,增加對于 onFulfilled 和 onRejected 方法的判斷: 實作 promise/A+ 規範的 promise 完整代碼可以參考  promise4.js 如何知道你實作的 promise 是否遵循 promise/A+ 規範呢?可以利用 promises-aplus-tests 這樣一個 npm 包來進行相應測試。

其他 API

這裡對其他常用的 promise API 進行了實作

1. catch、finally

2. Promise.resolve

傳回一個 resolved 狀态的 Promise 對象

3. Promise.reject

傳回一個 rejected 狀态的 Promise 對象

4. Promise.race

傳回一個 promise ,一旦疊代器中的某個 promise 狀态改變,傳回的 promise 狀态随之改變

5. Promise.all

傳回一個 promise ,隻有疊代器中的所有的 promise 均變為 fulfilled ,傳回的 promise 才變為 fulfilled ,疊代器中出現一個 rejected ,傳回的 promise 變為 rejected

6. Promise.allSettled

隻有等到疊代器中所有的 promise 都傳回,才會傳回一個 fulfilled 狀态的 promise ,并且傳回的 promise 狀态總是 fulfilled ,不會傳回 rejected 狀态   參考 •  MDN-Promise: https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Promise •  ECMAScript 6 入門-Promise 對象: https://es6.ruanyifeng.com/#docs/promise •  【翻譯】Promises/A+規範: https://www.ituring.com.cn/article/66566 •  完整的示例和測試代碼: github/lvqq/promise - END -

promise的狀态以及api介紹_如何實作一個 promise