promise/A+規範翻譯以及手寫實現

若是要手寫實現promise,那麼先看看promise/A+規範,再來實現,將會事半功倍。
那麼我先翻譯一下Promise/A+規範中的內容。git

  1. 術語
    1.1 promise 是一個帶有符合此規範的then方法的對象或者函數。
    1.2 thenable 是一個定義了一個then方法的對象或者函數。
    1.3 value 是一個任意合法的JavaScript值(包括undefined, thenable,或者promise)。
    1.4 exception 是一個使用throw語句拋出的值。
    1.5 reason 是一個指出爲何promise被rejected的值。
  2. 要求
    2.1 promise 狀態 github

    一個promise必須是三種狀態其中的一種狀態:pending,fulfilled 或者 rejected。 算法

    2.1.1 當promise處於pending狀態時:npm

    2.1.1.1 能夠轉變到 fulfilled 或者 rejected 狀態。

    2.1.2 當promise處於fulfilled狀態時:promise

    2.1.2.1 必定不可以轉變到其餘任何一種狀態。
       2.1.2.2 必須有一個value,而且這個值必定不能改變。

    2.1.3 當promise處於rejected狀態時:異步

    2.1.3.1 必定不可以轉變到其餘任何一種狀態。
       2.1.3.2 必須有一個reason,而且這個值不可以改變。

    在這裏,「必定不能改變」意味着不可變的身份(即===),但並不意味着深層不變性。(我的理解爲是value/reason指向的地址是不可變的,但倘若value/reason爲一個對象,則對象內的值是可變的。)函數

    2.2 then 方法
    一個promise必須提供一個then方法去訪問當前或者最終的value或者reason。
    一個promise的then方法接受兩個參數:測試

    promise.then(onFulfilled, onRejected)

    2.2.1 onFulfilled 和 onRejected 都是可選的參數:this

    2.2.1.1 若是 onFulfilled  不是一個函數,它必須被忽略。
        2.2.1.2 若是 onRejected 不是一個函數,它必須被忽略。

    2.2.2 若是 onFulfilled 是一個函數:spa

    2.2.2.1 promise 是 fulfilled 狀態時它必須被調用,而且 promise 的 value 做爲它的第一個參數。
       2.2.2.2 它必定不能在 promise 進入 fulfilled 狀態以前被調用。
       2.2.2.3 它必定不可以調用超過一次。

    2.2.3 若是 onRejected 時一個函數:

    2.2.3.1 promise 是 rejected 狀態時它必須被調用,而且 promise 的 reason 做爲它的第一個參數。
       2.2.3.2 它必定不能在 promise 進入 rejected 狀態以前被調用。
       2.2.3.3 它必定不可以調用超過一次。

    2.2.4 直到執行上下文堆棧僅包含平臺代碼以前,onFulfilled 或 onRejected 不可以被調用。[3.1]
    2.2.5 onFulfilled 和 onRejected 必須以函數的形式被調用(即沒有this值)。[3.2]
    2.2.6 then 能夠在同一個promise被調用屢次:

    2.2.6.1 當 promise 處於 fulfilled 狀態時,各個 onFulfilled 回調必須按其原始調用的順序執行。
       2.2.6.2 當 promise 處於 rejected 狀態時,各個 onRejected 回調必須按其原始調用的順序執行。

    2.2.7 then 必須返回一個promise [3.3]:

    promise2 = promise1.then(onFulfilled, onRejected);
    2.2.7.1 若是 onFulfilled 或 onRejected 返回一個值 x,運行Promise解決程序 [[Resolve]](promise2, x)。
       2.2.7.2 若是 onFulfilled 或 onRejected 拋出一個意外 e,promise2 必須以 e 爲 reason 被 rejected。
       2.2.7.3 若是 onFulfilled 不是一個函數而且 promise1 處於 fulfilled 狀態,promise2 必須以與 promise1 一樣的 value
               轉變到 fulfilled 狀態。
       2.2.7.4 若是 onRejected 不是一個函數而且  promise1 處於 rejected狀態,promise2 必須以與 promise1 一樣的 reason
               轉變到 rejected狀態。

    2.3 Promise解決程序

    promise解決程序是一個抽象的操做,它把一個 promise 和一個 value 做爲輸入,咱們將這個表示爲 [[Resolve]](promise, x)。若是 x 是一個 thenable ,它將會試圖讓 promise 採用 x 的狀態,前提是x的行爲至少有點像一個 promise。不然,它將會用值 x 執行 promise。
    對這些 thenable 的處理使得與 promise 實現方式可以去互相操做。只要它們公開了符合 Promise/A+ 的 then 方法。它還使得 promises/A+ 實現方式可以採用合理的 then 方法去「同化」不一致的實現方式。
    爲了運行[[Resolve]](promise, x),執行如下步驟:
    2.3.1 若是 promise 與 x 是同一個對象,以 Tyeperror 做爲 reason 去 reject promise。
    2.3.2 若是 x 是一個 promise,使用它的狀態:

    2.3.2.1 若是 x 處於 pending 狀態,promise 必須保持 pending 狀態直到 x 處於 fulfilled 或者 rejected 狀態。
       2.3.2.2 若是 x 處於 fulfilled 狀態,以相同的 value 去 fulfill promise。
       2.3.2.3 若是 x 處於 rejected 狀態,以相同的 reason 去 reject promise。

    2.3.3 不然,若是 x 是一個對象或者函數:

    2.3.3.1 讓 then 做爲 x.then。
       2.3.3.2 若是取屬性 x.then 會致使拋出異常 e,則以 e 爲 reason reject promise。
       2.3.3.3 若是 then 是一個函數,讓 x 做爲 this 調用它,第一個參數爲 resolvePromise,第二個參數爲 rejectPromise,而後:
           2.3.3.3.1 若是使用value y 調用 resolvepromise 時,運行[[Resolve]](promise, y)。
           2.3.3.3.2 若是使用reason r 調用 rejectPromise 時,也用 r reject promise。
           2.3.3.3.3 若是 resolvePromise 和 rejectPromise 都被調用了,或屢次調用同一參數,那麼第一個調用優先,其餘的調用都會被忽略。
           2.3.3.3.4 若是調用 then 的過程當中拋出了一個意外 e,
               2.3.3.3.4.1 若是 resolvePromise 或者 rejectPromise 被調用了,那麼忽略它。
               2.3.3.3.4.2 不然,把 e 做爲 reason reject promise。
       2.3.3.4 若是 then 不是一個函數,將 x 做爲參數執行 promise。

    2.3.4 若是 x 不是一個對象或者函數,將 x 做爲參數執行 promise。

    若是一個參與了 thenable 循環鏈的 thenable 去 resolve promise,這樣 [[Resolve]](promise, thenable) 的遞歸性質最終會致使 [[Resolve]](promise, thenable) 會被再次調用,遵循上述算法將會致使無限遞歸。咱們鼓勵去實現(但不是必需的)檢測這樣的遞歸,並以 TypeError 做爲 reason 去 reject Promise。[3.6]

  3. 注意
    3.1 這裏的「平臺代碼」指的是引擎,環境和 promise 實現代碼。實際上,這個要求保證了 onFulfilled 和 onRejected 將會異步執行,在事件循環以後,用一個新的堆棧來調用它。 這能夠經過「宏任務」機制(如 settimeou t或 setimmediate )或「微任務」機制(如 mutationobserver 或 process.nextick)來實現。因爲 Promise 實現被視爲平臺代碼,所以它自己可能包含一個任務調度隊列或「trampoline」,並在其中調用處理程序。
    3.2 也就是說,在 strict 模式下,這(指的是this)在它們內部將會是 undefined;在 sloppy 模式下,它將會是全局對象。
    3.3 若是實現知足全部要求,則實現可能容許 promise2 == promise1。每一個實現都應該記錄它是否可以生成 promise2 == promise1 以及在什麼條件下。
    3.4 通常來講,只有當 X 來自當前的實現時,才知道它是一個真正的 promise。本條款容許使用特定於實現的方法來採用已知一致承諾的狀態。
    3.5 此過程首先存儲對 x 的引用,而後測試該引用,而後調用該引用,避免屢次訪問 x.then 屬性。這些預防措施對於確保訪問器屬性的一致性很是重要,訪問器屬性的值可能在兩次檢索之間發生更改。
    3.6 實現方式中不該當在 thenbale 鏈中的深度設置主觀的限制,而且不該當假設鏈的深度超過主觀的限制後會是無限的。只有真正的循環才能致使 TypeError。若是遇到由無限多個不一樣 thenable 組成的鏈,那麼永遠遞歸是正確的行爲。

接下來根據規範進行手寫實現,註釋偷懶就將對應的規範標註出來,其實基本上就是對着規範實現。

function promise(fuc){ //接收一個函數做爲參數
    let that = this;
    that.value = null; // 2.1.2.2
    that.reason = null;// 2.1.3.2
    that.onFulfilled = []; // 2.2.6
    that.onRejected = []; //2.2.6
    that.status = 'pending'; // 2.1

    function resolve(val){
        if (that.status === 'pending')
        {
            // 2.1.2
            that.status = 'fulfilled';
            that.value = val; 
            that.onFulfilled.forEach(fc => fc(val)); //2.2.6.1
        }
    }
    function reject(err){
        if (that.status === 'pending')
        {
            //2.1.3
            that.status = 'rejected';
            that.reason = err;
            that.onRejected.forEach(fc => fc(err)); //2.2.6.2
        }
    }

    try {
        fuc(resolve,reject);
    } catch (error) {
        reject(error);
    }
}

promise.prototype.then = function (onFulfilled, onRejected) //2.2
{
    let that = this,
        promise2; //2.2.7

    onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : (x) => x; //2.2.1 2.2.7.3
    onRejected = typeof onRejected === 'function' ? onRejected : (e) => { throw e }; // 2.2.7.4

    switch (that.status) {
        case 'pending':
            promise2 = new promise((reslove, reject)=>{
                that.onFulfilled.push(()=>{
                    setTimeout(() => {
                        try {
                            let x = onFulfilled(that.value); //2.2.2.1
                            ResolutionProcedure(promise2, x, reslove, reject); //2.2.7.1
                        } catch (err) {
                            reject(err); //2.2.7.2
                        }    
                    }, 0);
                });
                that.onRejected.push(()=>{
                    setTimeout(() => {
                        try {
                            let x = onRejected(that.reason); //2.2.3.1
                            ResolutionProcedure(promise2, x, reslove, reject); //2.2.7.1
                        } catch (err) {
                            reject(err); //2.2.7.2
                        }   
                    }, 0);
                });
            })
            break;
        case 'fulfilled':
            promise2 = new promise((reslove,reject)=>{
                setTimeout(() => { // 2.2.4
                    try{
                        let x = onFulfilled(that.value); //2.2.2.1
                        ResolutionProcedure(promise2, x, reslove, reject); //2.2.7.1
                    }catch(err){
                        reject(err); //2.2.7.2
                    }    
                }, 0);
            })
            break;
        case 'rejected':
            promise2 = new promise((reslove, reject)=>{
                setTimeout(() => { // 2.2.4
                    try{
                        let x = onRejected(that.reason); //2.2.3.1
                        ResolutionProcedure(promise2, x, reslove, reject); //2.2.7.1
                    }catch(err){
                        reject(err); //2.2.7.2
                    }    
                }, 0);
            })
            break;
        default:
            break;
    }

    return promise2;
}

function ResolutionProcedure(promise, x, reslove, reject){ //2.3
    if (promise === x) //2.3.1
        return reject(new TypeError("same promise"));
    if (x instanceof Promise) //2.3.2
        x.then(((xvalue)=>{
            ResolutionProcedure(promise, xvalue, reslove, reject);
        },(xreason)=>{
            reject(xreason)
        }));
    if (x !== null &&( typeof x === 'object' || typeof x === 'function' )) //2.3.3
    {
        try {
            let then = x.then;//2.3.3.1
            if(typeof then === 'function') //2.3.3.3
            {
                let isuse = false;
                try {
                    then.call(x,(y)=>{
                        if (!isuse)
                        {   
                            ResolutionProcedure(promise, y, reslove, reject); //2.3.3.3.1
                            isuse = true;                        
                        }
                    },(r)=>{
                        if (!isuse) {
                            reject(r) //2.3.3.3.2
                            isuse = true;
                        }
                    })    
                } catch (error) {
                    if(!isuse)
                        reject(error) //2.3.3.3.4.2
                }
            }else{
                reslove(x); //2.3.3.4
            }
        } catch (error) {
            reject(error); //2.3.3.2
        }
    }else{
        reslove(x); //2.3.4
    }
}

作完以後能夠經過 promise test 進行測試。
先在代碼下面加上一些測試須要代碼。

promise.deferred = function () {
    let def = {};
    def.promise = new promise(function (resolve, reject) {
        def.resolve = resolve;
        def.reject = reject;
    });
    return def;
}
module.exports = promise

而後跑一下下面的代碼便可。

npm install -g promises-aplus-tests 

promises-aplus-tests  promise.js

結果所有經過,說明符合規範:
圖片描述


參考:連接實現一個完美符合Promise/A+規範的Promise

相關文章
相關標籤/搜索