Promise 原理解析 (漸進實例詳解)

前言

Promise 前端開發或多或少都有了解或使用到。 抽空梳理了一下,閱讀本章希望能幫助瞭解Promise到底是怎麼實現的。 我們採用漸進法,通過Demo 逐步實現Promise來體會Promise的用法。
Promise 規範有很多, 如 Promise/A, Promise/B, Promise/D == 以及 Promise/A 的升級 Promise/A+ , 有興趣可以去了解下。 最終ES6中採用了 Promise/A+ 規範。 所以本篇文章是按照Promise/A+== 規範來編寫的。(不想看英文版的轉至 Promise/A+ 規範中文翻譯

先來拋個磚

我們從一個場景開始講解。 考慮下面demo 獲取用戶賬號的請求處理:

//不使用Promise        
ajax.get('some_url', function (result) {
    //do something
    console.log(result.accountNumber );
});

//使用Promise
new Promise(function (resolve) {
    //異步請求
    ajax.get('some_url', function (results) {
        resolve(results.accountNumber )
    })
}).then(function (accountNumber ) {
    //do something
    console.log(accountNumber );
})

看起來好像不適用Promise 更簡潔下。 非也! 設想一下, 如果好幾個依賴前置請求都是異步, 此時如果沒有Promise , 那回調函數要一層一層嵌套, 看起來就很不舒服了。
如下:

//不使用Promise        
ajax.get('some_url', function (accountNumber ) {
    //do something
    ajax.get('getId', id, function (name) {
        //do something
        ajax.get('getName', name, function (course) {
            //dong something
            ajax.get('getCourse', function (courseDetail) {
                //do something
            })
        })
    })
});

//使用Promise
function getAccountNumber (url) {
    return new Promise(function (resolve) {
        //異步請求
        ajax.get(url, function (accountNumber ) {
            resolve(accountNumber )
        })
    })
}
getUserId('some_url').then(function (accountNumber ) {
    //do something
    return getId(accountNumber );
}).then(function (name) {
    //do something
    return getName(name);
}).then(function (course) {
    //do something
    return getCourse(course);
}).then(function (courseDetail) {
    //do something
});

來引個玉 (實現原理漸進)

說到底, Promise 也還是使用回調函數, 只不過是把回調封裝在了內部, 我們使用上一直通過then方法的鏈式調用, 書寫和理解上會更加直觀和簡潔。

1. 簡單的實現

//簡單的實現
class Promise {
    callbacks = [];
    constructor(fn) {
        fn(this._resolve.bind(this));
    }
    then(onFulfilled) {
        this.callbacks.push(onFulfilled);
    }
    _resolve(value) {
        this.callbacks.forEach(fn => fn(value));
    }
}

//Promise應用
let p = new Promise(resolve => {
    setTimeout(() => {
        console.log('done');
        resolve('6秒');
    }, 6000);
}).then((tip) => {
    console.log(tip);
})

這個簡單版本大致邏輯是:

實例化Promise 時,其類構造函數初始化執行了回調函數,並將綁定了當前實例的==_resolve方法作爲參數回傳給回到函數。接着調動Promise對象的then方法, 註冊異步操作完成後的回調函數。 當異步操作完成時,調用resolve方法, 該方法執行then==方法註冊的回調函數。

這裏then 方法註冊完成時的回到是一個數組, so then方法可以多次調用。註冊的函數會在異步操作完成後根據添加的順序依次執行。
如下:

//then 的說明
let p = new Promise(resolve => {
    setTimeout(() => {
        console.log('done');
        resolve('6秒');
    }, 6000);
});

p.then(tip => {
    console.log('then1', tip);
});

p.then(tip => {
    console.log('then2', tip);
});

仔細的同學可以看出, then方法應該是鏈式調用。 上面的調用方式就顯得有點囉嗦了。 我們來修改下我們的簡單實現源碼, 即在then 中 return this 就OK了。
實例:

//極簡的實現+鏈式調用
class Promise {
    callbacks = [];
    constructor(fn) {
        fn(this._resolve.bind(this));
    }
    then(onFulfilled) {
        this.callbacks.push(onFulfilled);
        return this;//看這裏
    }
    _resolve(value) {
        this.callbacks.forEach(fn => fn(value));
    }
}

let p = new Promise(resolve => {
    setTimeout(() => {
        console.log('done');
        resolve('6秒');
    }, 6000);
}).then(tip => {
    console.log('then1', tip);
}).then(tip => {
    console.log('then2', tip);
});

2. 加入延時機制

問題來了, 上面的Promise 實現如果在then方法註冊回調函數之間,resolve就執行了, 腫麼辦? 這樣回調函數就不會執行到了。 比如我們把上慄中setTimeout去掉:

//同步執行了resolve
let p = new Promise(resolve => {
    console.log('同步執行');
    resolve('同步執行');
}).then(tip => {
    console.log('then1', tip);
}).then(tip => {
    console.log('then2', tip);
});

執行結果只打印顯示了 “同步執行”。 再回去看下Promise的源碼, 發現resolve執行時, callbacks 還是空數組, 回調函數還沒來得急註冊上去。

這顯然是不允許的。 Promises/A++規範明確要求回調需要通過異步方式執行, 用以保證一致可靠的執行順序。 那我們加入一些處理,保證在resolve執行之前,then方法已經註冊完所有回調。 在來改造下我們resolve函數:

//極簡的實現+鏈式調用+延遲機制
class Promise {
    callbacks = [];
    constructor(fn) {
        fn(this._resolve.bind(this));
    }
    then(onFulfilled) {
        this.callbacks.push(onFulfilled);
        return this;
    }
    _resolve(value) {
        setTimeout(() => {//看這裏
            this.callbacks.forEach(fn => fn(value));
        });
    }
}

resove 中增加定時器, 通過定時器,將resolve 中執行的回調函數放置到JS任務隊列末尾, 以保證在resolve執行時, then方法的回調函數已經註冊完成。

當然這樣依然存在一個問題, 在resolve 執行後, 在通過 then 註冊上來的回調好像都沒有機會執行了!!!! 如下示例: 加上延遲後 then1 , then2 可以打印出來, 但是 then3 依然不OK。

let p = new Promise(resolve => {
    console.log('同步執行');
    resolve('同步執行');
}).then(tip => {
    console.log('then1', tip);
}).then(tip => {
    console.log('then2', tip);
});

setTimeout(() => {
    p.then(tip => {
        console.log('then3', tip);
    })
});

所以接下來我們需要增加一個狀態,並且保存 resolve 的值。 go on~

3. 加入狀態

爲了解決上面提到的問題, 我們需要加入狀態機制, 也就是大家熟知的pending, fulfilled, rejected

Promises/A+ 規範中明確規定了, pending 可以轉化爲fulfilledrejected 並且只能轉化一次。 也就是說如果pending 轉爲 fulfiled 就不能再轉化到rejected。 並且 fulfilled 和 rejected 狀態只能pending轉化而來, 兩者之間不能互相轉換。

在這裏插入圖片描述
增加狀態後的代碼爲:

//簡單的實現+鏈式調用+延遲機制+狀態
class Promise {
    callbacks = [];
    state = 'pending';//增加狀態
    value = null;//保存結果
    constructor(fn) {
        fn(this._resolve.bind(this));
    }
    then(onFulfilled) {
        if (this.state === 'pending') {//在resolve之前,跟之前邏輯一樣,添加到callbacks中
            this.callbacks.push(onFulfilled);
        } else {//在resolve之後,直接執行回調,返回結果了
            onFulfilled(this.value);
        }
        return this;
    }
    _resolve(value) {
        this.state = 'fulfilled';//改變狀態
        this.value = value;//保存結果
        this.callbacks.forEach(fn => fn(value));
    }
}

resolve 執行時, 會將狀態設置爲 fulfilled , 並把 value 的值存起來, 在此之後調用 then 添加的新回調都會立即執行, 直接返回保存的value值。

注: 當增加完狀態之後, 原先的_resolve中的定時器可以去掉了。 當resolve 同步執行, 雖然callbacks 爲空, 回調函數還沒來得及註冊, 但沒關係, 因爲後面註冊上來時, 判斷狀態爲 fulfilled後, 會立即遍歷執行回調。

4. 真正的鏈式調用

有個非非非常重要的問題, 優秀的你應該發現了, 上面提到的鏈式調用,只是在then方法中 **return 了 this **, 使得 Promise 實例可以多次調用 then方法,但因爲是同一個實例, 調用再多次then也只能返回相同的一個結果。 SO , 這裏滿足不了我們真正的使用需求。

真正的鏈式Promise 是指在當前Promise 達到 fulfilled 狀態後, 即開始進行下一個Promise (後鄰Promise)。 那麼我們如何銜接當前Promise 和後鄰Promise呢? 這裏是重點!!!

先來看下完整的實現源碼:

//完整的實現
class Promise {
    callbacks = [];
    state = 'pending';//增加狀態
    value = null;//保存結果
    constructor(fn) {
        fn(this._resolve.bind(this));
    }
    then(onFulfilled) {
        return new Promise(resolve => {
            this._handle({
                onFulfilled: onFulfilled || null,
                resolve: resolve
            });
        });
    }
    _handle(callback) {
        if (this.state === 'pending') {
            this.callbacks.push(callback);
            return;
        }
        //如果then中沒有傳遞任何東西
        if (!callback.onFulfilled) {
            callback.resolve(this.value);
            return;
        }
        var ret = callback.onFulfilled(this.value);
        callback.resolve(ret);
    }
    _resolve(value) {

        if (value && (typeof value === 'object' || typeof value === 'function')) {
            var then = value.then;
            if (typeof then === 'function') {
                then.call(value, this._resolve.bind(this));
                return;
            }
        }

        this.state = 'fulfilled';//改變狀態
        this.value = value;//保存結果
        this.callbacks.forEach(callback => this._handle(callback));
    }
}

先來 簡單說說要點,然後在逐一通過例子去詳解。

  • then 方法中, 創建返回了新的Promise 實例, 這是串行Promise的基礎, 是實現真正鏈式調用的根本。
  • handle 方法是Promise 內部的方法。 then 方法傳入的形參onFulfilled以及新創建Promise 實例時傳入的resolve勻被 push 到當前 Promise 的callbacks
    隊列中, 這是銜接當前 promise 和 後鄰 promise 的關鍵。
  • resolve 方法中會先檢查value 是不是 Promise 對象, 如果是一個新的 Promise , 那麼先不改變當前 promise 的狀態。

來一個栗子(這裏直貼主要代碼片段,能理解即可):

/**
 * 模擬異步請求
 * @param {*} url  請求的URL
 * @param {*} s  指定該請求的耗時,即多久之後請求會返回。單位秒
 * @param {*} callback 請求返回後的回調函數
 */
const mockAjax = (url, s, callback) => {
    setTimeout(() => {
        callback(url + '異步請求耗時' + s + '秒');
    }, 1000 * s)
}


//Demo1
new Promise(resolve => {
    mockAjax('getId', 1, function (result) {
        resolve(result);
    })
}, 'first').then(result => {
    console.log(result);
});

爲了清楚的看到當前是哪個實例, 在Promise 的實現中, 增加了一個 name 屬性, 標識當前 Promise 實例, 上慄中傳了‘first’表示第一個Promise實例。
下面爲輸入日誌:

Promise[first]:constructor
Promise[first]:then
Promise[then1]:constructor
Promise[first]:_handle state= pending
Promise[first]:_handle callbacks= [ { onFulfilled: [Function], resolve: [Function] } ]
=> Promise { callbacks: [], name: 'then1', state: 'pending', value: null }
Promise[first]:_resolve
Promise[first]:_resolve value= getUserId異步請求耗時1秒
Promise[first]:_handle state= fulfilled
getUserId異步請求耗時1秒
Promise[then1]:_resolve
Promise[then1]:_resolve value= undefined

通過上面打印日誌可以看到:

  1. 構造first實例,立即執行mackAjax(‘getUserId’,callback);
  2. 調用first的then方法,註冊first完成時的回調函數。
  3. then函數內部構造了一個新的Promise實例:then1。立即執行first的_handle方法
  4. 此時first還是pending的狀態
  5. first._handle中就把註冊在first成功後的回調和then1的狀態改變操作接口保存在first內 部
  6. 至此當前線程執行結束。返回的是then1的Promise實例。
  7. 1s後,異步請求返回,要改變first的狀態和結果,執行resolve(result)
  8. first的值被改變,內容爲異步請求返回的結果:“getUserId異步請求耗時1s”
  9. first的狀態變成fulfilled
  10. first註冊的成功回調被執行,打印出了"getUserId異步請求耗時1秒"
  11. 然後再調用then1._resolve
  12. 改變then1的值和狀態,因爲first註冊的回調函數沒有返回值,所以then1的值爲undefined

這裏補一張直觀的流程圖

在這裏插入圖片描述
如果我們吧上面異步的請求改成同步會是什麼結果呢?

new Promise(resolve => {
    resolve('getUserId同步請求');
}, 'first').then(result => {
    console.log(result);
});

//打印日誌
Promise[first]:constructor
Promise[first]:_resolve
Promise[first]:_resolve value= getUserId同步請求
Promise[first]:then
Promise[then1]:constructor
Promise[first]:_handle state= fulfilled
getUserId同步請求
Promise[then1]:_resolve
Promise[then1]:_resolve value= undefined
=> Promise {
  callbacks: [],
  name: 'then1',
  state: 'fulfilled',
  value: undefined }

感興趣的可以自己去分析一下。

至此, 應該基本能夠理解真正的鏈式調用了吧。 如果是多個then 時,要知道它是可以無限寫下去的, 上一級 onFulfilled return 的值, 會變成下一級onFulfilled的結果

那麼接下來我們對比下下面的兩個Demo:

Demo2

//Demo2
new Promise(resolve => {
    mockAjax('getUserId', 1, function (result) {
        resolve(result);
    })
}, 'first').then(result => {
    console.log(result);
    //對result進行第一層加工
    let exResult = '前綴:' + result;
    return exResult;
}).then(exResult => {
    console.log(exResult);
    let xxResult = exResult + ':後綴';
    return xxResult;
}).then(finalResult => {
    console.log(finalResult);
});

Demo3

//Demo3 === Demo2
new Promise(resolve => {
    mockAjax('getUserId', 1, function (result) {
        resolve(result);
    })
}, 'first').then(result => {
    console.log(result);
    //對result進行第一層加工
    let exResult = '前綴:' + result;
    console.log(exResult);

    let finalResult = exResult + ':後綴';
    console.log(finalResult);
});

感覺demo3 的寫法更好些!!! 那鏈式調用的意義在哪裏?

仔細的同學一定發現了, 目前爲止Promise 源碼中還有一塊代碼沒有被執行到。 也就是下面 _resolve 中最開始的 if 代碼塊, 好像從來沒有被執行到。

 _resolve(value) {

        if (value && (typeof value === 'object' || typeof value === 'function')) {
            var then = value.then;
            if (typeof then === 'function') {
                then.call(value, this._resolve.bind(this));
                return;
            }
        }

        this.state = 'fulfilled';//改變狀態
        this.value = value;//保存結果
        this.callbacks.forEach(callback => this._handle(callback));
    }

從代碼上看, 它是對resolve 中的值做了一個特殊的判斷, 判斷resolve的值是否爲Promise 實例, 如果是Promise 實例, 那麼就把當前Promise實例的改變狀態的接口重新註冊到resolve的值Promise 的成功回調中, 也就是說當前 Promise 實例的狀態要依賴resolve的值的Promise實例的狀態。

//Demo4
const getId = function () {
    new Promise(resolve => {
        mockAjax('getId', 1, function (result) {
            resolve(result);
        })
    }, 'getUserId');
}
const getId = function () {
    new Promise(resolve => {
        mockAjax('getId', 2, function (result) {
            resolve(result);
        })
    }, 'getId');
}
const getName = function () {
    new Promise(resolve => {
        mockAjax('getName', 3, function (result) {
            resolve(result);
        })
    }, 'getName');
}
const getCourse = function () {
    new Promise(resolve => {
        mockAjax('getCourse', 4, function (result) {
            resolve(result);
        })
    }, 'getCourse');
}
getId()
    .then(id => {
        console.log(id);
        return geId();
    }).then(name => {
        console.log(name);
        return getName()
    }).then(course => {
        console.log(course);
        return getCourse()
    }).then(courseDetail => {
        console.log(courseDetail);
    });

運行結果如下:

Promise[getId]:constructor
Promise[getId]:then
Promise[then1]:constructor
Promise[getId]:_handle state= pending
Promise[getId]:_handle callbacks= [ { onFulfilled: [Function], resolve: [Function] } ]
Promise[then1]:then
Promise[then2]:constructor
Promise[then1]:_handle state= pending
Promise[then1]:_handle callbacks= [ { onFulfilled: [Function], resolve: [Function] } ]
Promise[then2]:then
Promise[then3]:constructor
Promise[then2]:_handle state= pending
Promise[then2]:_handle callbacks= [ { onFulfilled: [Function], resolve: [Function] } ]
Promise[then3]:then
Promise[then4]:constructor
Promise[then3]:_handle state= pending
Promise[then3]:_handle callbacks= [ { onFulfilled: [Function], resolve: [Function] } ]
=> Promise { callbacks: [], name: 'then4', state: 'pending', value: null }
Promise[getId]:_resolve
Promise[getId]:_resolve value= getId異步請求耗時1秒
Promise[getId]:_handle state= fulfilled
getId異步請求耗時1秒
Promise[getId]:constructor
Promise[then1]:_resolve
Promise[then1]:_resolve value= Promise {
  callbacks: [],
  name: 'getId',
  state: 'pending',
  value: null }
Promise[getId]:then
Promise[then5]:constructor
Promise[getId]:_handle state= pending
Promise[getId]:_handle callbacks= [ { onFulfilled: [Function], resolve: [Function] } ]
Promise[getId]:_resolve
Promise[getId]:_resolve value= getId異步請求耗時2秒
Promise[getId]:_handle state= fulfilled
Promise[then1]:_resolve
Promise[then1]:_resolve value= getId異步請求耗時2秒
Promise[then1]:_handle state= fulfilled
getId異步請求耗時2秒
Promise[getName]:constructor
Promise[then2]:_resolve
Promise[then2]:_resolve value= Promise {
  callbacks: [],
  name: 'getName',
  state: 'pending',
  value: null }
Promise[getName]:then
Promise[then6]:constructor
Promise[getName]:_handle state= pending
Promise[getName]:_handle callbacks= [ { onFulfilled: [Function], resolve: [Function] } ]
Promise[then5]:_resolve
Promise[then5]:_resolve value= undefined
Promise[getName]:_resolve
Promise[getName]:_resolve value= getName異步請求耗時3秒
Promise[getName]:_handle state= fulfilled
Promise[then2]:_resolve
Promise[then2]:_resolve value= getName異步請求耗時3秒
Promise[then2]:_handle state= fulfilled
getName異步請求耗時3秒
Promise[getCourse]:constructor
Promise[then3]:_resolve
Promise[then3]:_resolve value= Promise {
  callbacks: [],
  name: 'getCourse',
  state: 'pending',
  value: null }
Promise[getCourse]:then
Promise[then7]:constructor
Promise[getCourse]:_handle state= pending
Promise[getCourse]:_handle callbacks= [ { onFulfilled: [Function], resolve: [Function] } ]
Promise[then6]:_resolve
Promise[then6]:_resolve value= undefined
Promise[getCourse]:_resolve
Promise[getCourse]:_resolve value= getCourse異步請求耗時4秒
Promise[getCourse]:_handle state= fulfilled
Promise[then3]:_resolve
Promise[then3]:_resolve value= getCourse異步請求耗時4秒
Promise[then3]:_handle state= fulfilled
getCourse異步請求耗時4秒
Promise[then4]:_resolve
Promise[then4]:_resolve value= undefined
Promise[then7]:_resolve
Promise[then7]:_resolve value= undefined

錯誤處理

在異常操作失敗時,標記其狀態爲rejected , 並執行註冊的失敗回調。

//demo reject
new Promise((resolve, reject) => {

    mockAjax('getId', 1, function (result, error) {
        if (error) {
            reject(error)
        } else {
            resolve(result);
        }
    })

}, 'getId').then(result => {
    console.log(result);
}, error => {
    console.log(error);
});

有了之前處理fulfilled 狀態的經驗, 支持錯誤處理就變的很容易了, 只需要在註冊回調, 處理狀態變更上都加入新的 reject 邏輯:

//完整的實現+reject
class Promise {
    callbacks = [];
    state = 'pending';//增加狀態
    value = null;//保存結果
    constructor(fn) {
        fn(this._resolve.bind(this), this._reject.bind(this));
    }
    then(onFulfilled, onRejected) {
        return new Promise((resolve, reject) => {
            this._handle({
                onFulfilled: onFulfilled || null,
                onRejected: onRejected || null,
                resolve: resolve,
                reject: reject
            });
        });
    }
    _handle(callback) {
        if (this.state === 'pending') {
            this.callbacks.push(callback);
            return;
        }

        let cb = this.state === 'fulfilled' ? callback.onFulfilled : callback.onRejected;

        if (!cb) {//如果then中沒有傳遞任何東西
            cb = this.state === 'fulfilled' ? callback.resolve : callback.reject;
            cb(this.value);
            return;
        }

        let ret = cb(this.value);
        cb = this.state === 'fulfilled' ? callback.resolve : callback.reject;
        cb(ret);
    }
    _resolve(value) {

        if (value && (typeof value === 'object' || typeof value === 'function')) {
            var then = value.then;
            if (typeof then === 'function') {
                then.call(value, this._resolve.bind(this), this._reject.bind(this));
                return;
            }
        }

        this.state = 'fulfilled';//改變狀態
        this.value = value;//保存結果
        this.callbacks.forEach(callback => this._handle(callback));
    }
    _reject(error) {
        this.state = 'rejected';
        this.value = error;
        this.callbacks.forEach(callback => this._handle(callback));
    }
}

異常處理

上面介紹了錯誤處理, 是指在Promise 的構造函數中發現的錯誤, 並通過reject 通知的。 如果在執行成功或者失敗回調時出現了異常,該如何處理??
對於類似異常, 處理也很簡單, 可以使用try-catch 捕獲錯誤, 然後將相應的promise 狀態設置爲rejected 狀態。 我們來改造下==_handle==方法如下:

_handle(callback) {
        if (this.state === 'pending') {
            this.callbacks.push(callback);
            return;
        }

        let cb = this.state === 'fulfilled' ? callback.onFulfilled : callback.onRejected;

        if (!cb) {//如果then中沒有傳遞任何東西
            cb = this.state === 'fulfilled' ? callback.resolve : callback.reject;
            cb(this.value);
            return;
        }

        let ret;

        try {
            ret = cb(this.value);
            cb = this.state === 'fulfilled' ? callback.resolve : callback.reject;
        } catch (error) {
            ret = error;
            cb = callback.reject
        } finally {
            cb(ret);
        }

    }

無論是錯誤還是異常, 最終都是通過reject 實現的, 可見最終對於錯誤以及異常的處理, 都可以通過then中的錯誤回調來處理。 所以可以單獨增加一個catch 方法, 它是 .then(null, rejection) 的別名。
如下:

 then(onFulfilled, onRejected) {
        return new Promise((resolve, reject) => {
            this._handle({
                onFulfilled: onFulfilled || null,
                onRejected: onRejected || null,
                resolve: resolve,
                reject: reject
            });
        });
    }
    catch(onError){
      this.then(null, onError);
    }

Finally 方法

這個方法不用多說,想必大家都很熟。 不過Promise最後的狀態如何, 都要執行的操作。 finally 的實現其實很簡單:

catch(onError) {
    console.log('Promise[%s]:catch', this.name);
    this.then(null, onError);
  }
  finally(onDone){
    console.log('Promise[%s]:finally', this.name);
    this.then(onDone, onDone);
  }

靜態方法

除了上面提到的Promise 實例的方法外, Promise 還提供了 Promise.resolve 和 Promise.reject 方法。 用於將非Promise實例包裝爲Promise實例。

Promise.resolve('foo')
// 等價於
new Promise(resolve => resolve('foo'))

這裏 靜態方法有個特點: 如果其參數爲promise 則什麼都不做,返回參數; 如果參數爲一個非promise 對象, 則創建並返回一個promise

再來看看這個例子:

Promise.resolve(getNameById(id)).then(name => {
  console.log(name);
});

在實現Promise.resolve 之前, 先看下它的參數部分有哪些情況:

1.參數是一個Promise實例
如果參數是 Promise 實例,那麼Promise.resolve將不做任何修改、原封不動地返回這個實例。

2.參數是一個thenable對象
thenable對象指的是具有then方法的對象,比如下面這個對象。

let thenable = {
  then: function(onFulfilled) {
    onFulfilled(42);
  }
};

Promise.resolve方法會將這個對象轉爲 Promise 對象,然後就立即執行thenable對象的then方法。

let thenable = {
  then: function(onFulfilled) {
    onFulfilled(42);
  }
};

let p1 = Promise.resolve(thenable);
p1.then(function(value) {
  console.log(value);  // 42
});

上面代碼中,thenable對象的then方法執行後,對象p1的狀態就變爲resolved,從而立即執行最後那個then方法指定的回調函數,輸出 42。

3. 參數不是具有then方法的對象,或根本就不是對象
如果參數是一個原始值,或者是一個不具有then方法的對象,則Promise.resolve方法返回一個新的 Promise 對象,狀態爲resolved。

4. 不帶任務參數
Promise.resolve方法允許調用時不帶參數,直接返回一個resolved狀態的 Promise 對象。

 finally(onDone) {
    this.then(onDone, onDone);
  }
  static resolve(value) {
    if (value && value instanceof Promise) {
      return value;
    } else if (value && typeof value === 'object' && typeof value.then === 'function') {
      let then = value.then;
      return new Promise(resolve => {
        then(resolve);
      });

    } else if (value) {
      return new Promise(resolve => resolve(value));
    } else {
      return new Promise(resolve => resolve());
    }
  }

Promise.reject與Promise.resolve類似,區別在於Promise.reject始終返回一個狀態的rejected的Promise實例,而Promise.resolve的參數如果是一個Promise實例的話,返回的是參數對應的Promise實例,所以狀態不一 定。

Promise.resolve / Promise.reject/ Promise.catch 內部實現

// 使用
Promise.resolve('is resolve').then(data=>{
   console.log(data) // is resolve 
})

// 內部實現
Promise.resolve = function(value) {
   return new Promise((resolve, reject) => { // 內部new一個Promise對象
       resolve(value)
   })
}

// reject 內部實現
Promise.reject = function(resason) {
    return new Promise((resolve, reject) => {
        reject(reason)
    })
}

// catch 內部實現
Promise.reject('is  reject').catch(e => {
   console.log(e)  // is reject
})

// 上面這段代碼相當於下面這段代碼
Promise.reject('is reject').then(null, e =>{ // then裏直接走了失敗的回調
    console.log(e)  // is reject
})

// 內部實現
Promise.prototype.catch = function(onReected) {
    return this.then(null, onRejected) // 相當與then裏的成功回調只傳個null
}

** Promise.all 內部實現**

// Promise.all 這個方法非常重要, 同時執行多個異步,並返回一個新的promise, 成功的值是一個數組, 該數組成員的順序是傳給Promise.all的順序
// Promise.all 的使用
function usePromise(url, params){
   return new Promise((resolve, reject) => {
       setTimeout(function(err, data){
           if(err) reject(err)
           resolve(data)
        }, 2000)
   })
}

Promise.all(usePromise('url1',{}), usePromise(url2, {})).then(data => {
    console.log(data) // 全部讀取成功後返回[data1,  data2, ....]
                                 // 需要注意的是,其中有一個失敗,則走失敗的回調函數
})

// 內部實現
Promise.all = function(promises) {  // promises 是個數組
    let arr = [];
    let i = 0;
    function processData(index, data) {
       arr[index] = data
    
       if(++i == promises.length){
          resolve(arr)
       }
    }

     for(let i = 0; i<promise.length; i++) {
        promsies[i].then(data => {
          // 因爲Promise.all 最終返回的是一個數組成員按照順序排序的數組,
          // 而異步執行,返回並不一定按照順序, 所以需要傳當前的i
           processData(i, data);  
        }, reject) // 如果其中一個失敗,則調用reject
     }
}

Promise.race 內部實現

// 原生Promise.race的使用
// 一個成功就走成功的回調,一個失敗就走失敗的回調
Promise.race(promise1, promise2,...).then(data => {
    console.log(data);  // 可能返回promise1的結果, 也可能返回promise2的結果,看哪個返回的快就用哪個作爲結果
})

// 內部實現
Promise.race = function(promises) { // promises 是數組
    return new Promise((resolve, reject) => {
        for(let i=0; i<promise.length; i++) {
           promises[i].then(resolve, reject)  //
        }
    })
}

總結

剛開始看promise源碼的時候總不能很好的理解then和resolve函數的運行機理,但是如果反過來根據執行promise時的邏輯來推演,就不難理解了。這裏一定要注意的點是:promise裏面的then函數僅僅是註冊了後續需要執行的代碼,真正的執行是在resolve方法裏面執行的,理清了這層,再來分析源碼會省力的多。
現在回顧下Promise的實現過程,其主要使用了設計模式中的觀察者模式
通過Promise.prototype.then和Promise.prototype.catch方法將觀察者方法註冊到被觀察者Promise對象中,同時返回一個新的Promise對象,以便可以鏈式調用。
被觀察者管理內部pending、fulfilled和rejected的狀態轉變,同時通過構造函數中傳遞的resolve和reject方法以主動觸發狀態轉變和通知觀察者。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章