如何實現一個Promise

引言

Promise出現解決了js中的回調地獄的問題,使代碼更簡潔,是ES6中的規範和重要特性。它的使用很簡單,但你知道它是怎麼樣實現的嗎~~
如今咱們就來看一下Promise到底是怎麼樣實現的😄javascript

  • promise規範
    Promise規範是Promise函數中須要遵循的規則, ES6中使用的Promise,它就是遵循Promise/A+規範的。
    既然是有規則可循的,那咱們根據規則來一步步實現Promise

1.建立Promise類

看一下Promise是如何使用的java

const promise = new Promise((resolve, reject) => {
        try {
            resolve('123');
        } catch(err) {
            reject('error');
        }
    });
    
    promise
    .then((msg) => {
        console.log(msg)
    })
複製代碼
  • 首先它是一個構造方法,而且接收一個function做爲參數,
    而這個function中有resolvereject兩個方法。resolve表明成功後返回的值,reject表明拒絕返回的緣由

根據Promise的使用來建立一個叫MyPromise的類git

/** * @params {function} callback 須要執行的業務方法 */
    class MyPromise {
        // 構造方法接收一個function
        constructor(callback) {
            callback(this.resolve, this.reject); // 調用此function
        }
        resolve = (value) => {} // callback中執行的resolve方法
        reject = (reason) => {} // callback中執行的reject方法
    }
    
    // 測試
    var test = new MyPromise((resolve, reject) => {
        console.log('my promise is running!');
    }) // 打印出 my promise is running!
複製代碼

2.三種狀態

如今咱們建立的類已經能夠執行傳入的方法了,可是它傳入的resolvereject方法是有什麼用的呢?
咱們接着看Promise規範 github

  • 根據規範可知Promise有三種狀態 pending(等待),fulfilled(完成),rejected(拒絕)。
  • 當狀態爲pending時,Promise能夠變爲fulfilledrejected狀態
  • 當狀態爲fulfilled時,Promise不能改變其狀態;必須有值且不能改變
  • 當狀態爲rejected時,Promise不能改變其狀態;必須有拒絕的緣由且不能改變

根據Promise規則,接着寫剛剛建立的類:面試

const stateArr = ['pending', 'fulfilled', 'rejected']; // 三種狀態
    /** * @params {function} callback 須要執行的業務方法 */
    class MyPromise {
        constructor(callback) {
            this.state = stateArr[0]; // 當前狀態
            this.value = null; // 完成時的返回值
            this.reason = null; // 失敗緣由
            
            callback(this.resolve, this.reject); // 調用此function
        }
        
        // callback中執行的resolve方法
        resolve = (value) => {
            // 判斷狀態是否須要是pending
            if (this.state === stateArr[0]) {
               this.state = stateArr[1]; // 更新狀態爲 fulfilled
               this.value = value; // 寫入最終的返回值
            }
        }
        
        // callback中執行的reject方法
        reject = (reason) => {
            // 判斷狀態是否須要是pending
            if (this.state === stateArr[0]) {
               this.state = stateArr[2]; // 更新狀態爲 rejected
               this.reason = reason; // 寫入拒絕的緣由
            }
        } 
    }
複製代碼

測試一下: 數組

能夠看到,調用 resolve後,狀態變爲 fulfilled,再調用 reject時,狀態和值都不會改變,這樣符合Promise規範~~

3.then方法

咱們的MyPromise寫到這裏,他已經能夠實現更新狀態和傳值了,可是它的值怎麼樣輸出給咱們的業務呢?
Promise的使用能夠看到,它是經過then方法來輸出值的。then是是一個必要的方法,看一下then的規範:promise

  • promise必須提供一個then方法去訪問他的當前或最終的值或緣由
  • promise中then方法接收兩個參數 onFulilledonRejected

下面是關於onFulilledonRejected的規範(部分)異步

  • onFulilledonRejected二者都是一個可選的參數:
    • 若是onFulilled不是一個函數,它必須被忽視
    • 若是onRejected不是一個函數,它必須被忽視
  • 若是onFulilled是一個函數:
    • 它必須在fulfilled時被調用,promise方法中的value做爲第一個參數
    • 它必須在fulfilled以前被調用
    • 它不能被屢次調用
  • 若是onRejected是一個函數:
    • 它必須在rejected時被調用,promise方法中的reason做爲第一個參數
    • 它必須在rejected以前被調用
    • 它不能被屢次調用
  • 在執行上下文堆棧僅包含平臺代碼以前,不能調用onFulfilledonRejected
  • onFulfilledonRejected必須是一個函數
  • then能夠在同一個promise中屢次被調用
  • then 必須返回一個promise

根據then函數的規則,咱們來設計這個then方法函數

const stateArr = ['pending', 'fulfilled', 'rejected']; // 三種狀態
    class MyPromise {
        constructor(callback) {
            this.state = stateArr[0]; // 當前狀態
            this.value = null; // 完成時的返回值
            this.reason = null; // 失敗緣由
            
            callback(this.resolve, this.reject); // 調用此function
        }
        
        // callback中執行的resolve方法
        resolve = (value) => {
            // 判斷狀態是否須要是pending
            if (this.state === stateArr[0]) {
               this.state = stateArr[1]; // 更新狀態爲 fulfilled
               this.value = value; // 寫入最終的返回值
            }
        }
        
        // callback中執行的reject方法
        reject = (reason) => {
            // 判斷狀態是否須要是pending
            if (this.state === stateArr[0]) {
               this.state = stateArr[2]; // 更新狀態爲 rejected
               this.reason = reason; // 寫入拒絕的緣由
            }
        }
        
        // then方法
        then = (onFulilled, onRejected) => {
            // 判斷onFulilled 和 onRejected是不是一個函數,若是不是函數則忽略它
            onFulilled = typeof onFulilled === 'function' ? onFulilled : (value) => value;
            onRejected = typeof onRejected === 'function' ? onRejected : (reason) => reason;
            
            // 若是狀態是fulfilled
            if (this.state === stateArr[1]) {
                // then返回的必須是一個promise
                return new MyPromise((resolve, reject) => {
                    try {
                        const result = onFulilled(this.value); // 執行傳入的onFulilled方法
                        
                        // 若是onFulilled返回的是一個Promise,則調用then方法
                        if (result instanceof MyPromise) {
                            result.then(resolve, reject);
                        } else {
                            resolve(result);
                        }
                    } catch(err) {
                        reject(err);
                    }
                })
            }
            
            // 若是狀態是rejected
            if (this.state === stateArr[2]) {
                // then返回的必須是一個promise
                return new MyPromise((resolve, reject) => {
                    try {
                        const result = onRejected(this.reason); // 執行傳入的onRejected方法
                        
                        // 若是onRejected返回的是一個Promise,則調用then方法
                        if (result instanceof MyPromise) {
                            result.then(resolve, reject);
                        } else {
                            resolve(result);
                        }
                    } catch(err) {
                        reject(err);
                    }
                })
            }
        }
    }

複製代碼

測試一下:oop

成功返回:

失敗返回:

4.繼續完善

至此,咱們的MyPromise的已經基本能夠運行了,可是如今有一個很嚴重的缺陷,若是遇到異步的請求時候,resolve不能按上下文執行,這會致使then方法執行失敗例如

var test = new MyPromise((resolve, reject) => {
        setTimeout(() => {
            resolve(123);
        }, 2000)
    })
    .then(msg => {
        console.log(msg);
        return 456;
    })
複製代碼

由於在調用then方法的時候,promise的狀態尚未改變,而咱們的then方法尚未處理pending狀態的邏輯。 這致使執行異步方法的時候,then方法不會返回任何東西
好比,在上面的例子中,javscript已經把then方法執行了,但setTimeout中的方法還在eventloop中等待執行。 這樣須要作的是:

  • then中的方法保存起來,等待resolvereject執行後再調用剛剛保存的then中的方法
  • 因爲在這期間可能會有多個then方法會被執行,因此須要用一個數據來保存這些方法
    根據這兩點,再來修改一些代碼
// 在constructor中新增兩個數組分別用於裝then中的resolve和reject方法
    constructor(callback) {
        this.resolveArr = [];
        this.rejectArr = [];
    }
    
    // 修改resolve方法
    resolve = (value) => {
        // 判斷狀態是否須要是pending
            if (this.state === stateArr[0]) {
               this.state = stateArr[1]; // 更新狀態爲 fulfilled
               this.value = value; // 寫入最終的返回值
               
               this.resolveArr.forEach(fun => fun(value)) // 循環執行then已插入的resolve方法
            }
    }
    
    // 修改reject方法
    reject = (reason) => {
        // 判斷狀態是否須要是pending
            if (this.state === stateArr[0]) {
               this.state = stateArr[1]; // 更新狀態爲 fulfilled
               this.reason = reason; // 寫入最終的返回值
               
               this.rejectArr.forEach(fun => fun(reason)) // 循環執行then已插入的reject方法
            }
    }
    
    // then方法中須要添加捕捉pending狀態的邏輯
    then = (onFulilled, onRejected) => {
        // 若是狀態爲pending
        if (this.state === stateArr[0]) {
            return new Promise((resolve, reject) => {
                // 插入成功時調用的函數
                this.resolveArr.push((value) => {
                    try {
                        const result = onFulilled(value);
                        if (result instanceof MyPromise) {
                            result.then(resolve, reject);
                        } else {
                            resolve(result);
                        }
                    } catch(err) {
                        reject(err);
                    }
                })
                
                // 插入失敗時調用的函數
                this.rejectArr.push((value) => {
                    try {
                        const result = onRejected(value);
                        if (result instanceof MyPromise) {
                            result.then(resolve, reject);
                        } else {
                            resolve(result);
                        }
                    } catch(err) {
                        reject(err)
                    }
                })
            })
        }
    }
複製代碼

寫好了,測試一下~

new MyPromise((resolve, reject) => {
        setTimeout(() => {
            resolve(123);
        }, 2000)
    })
    .then(msg => {
        console.log(msg);
        return new MyPromise((resolve, reject) => {
			setTimeout(()=> {
				resolve(456)
			}, 2000);
		})
    })
    .then(msg => {
        console.log(msg);
    })
複製代碼

5.Promise的其餘方法

根據Promise規範實現的Promise大體已經完成啦,最後咱們把Promise中實現的方法也補一下

  • catch方法
// 在MyPromise原型中實現
    class MyPromise {
        // 調用then中的reject
        catch = (reject) => {
            this.then(null, reject);
        }
    }
複製代碼
  • resolve
MyPromise.resolve = (value) => {
        return new MyPromise((resolve, reject) => { resolve(value) });
    }
複製代碼
  • reject
MyPromise.resolve = (reason) => {
        return new MyPromise((resolve, reject) => { reject(reason) });
    }
複製代碼
  • 還有allracefinally(原型方法),其實都是根據Promise中的原型方法和Promise規則實現的,這裏就不一一列舉啦。須要瞭解的小夥伴能夠自行去看

最終代碼(源碼地址)

const stateArr = ['pending', 'fulfilled', 'rejected']; // 三種狀態
class MyPromise {
    constructor(callback) {
        this.state = stateArr[0]; // 當前狀態
        this.value = null; // 完成時的返回值
        this.reason = null; // 失敗緣由
        this.resolveArr = [];
        this.rejectArr = [];
        
        callback(this.resolve, this.reject); // 調用此function
    }
    
    // callback中執行的resolve方法
    resolve = (value) => {
        // 判斷狀態是否須要是pending
            if (this.state === stateArr[0]) {
                this.state = stateArr[1]; // 更新狀態爲 fulfilled
                this.value = value; // 寫入最終的返回值
               
                this.resolveArr.forEach(fun => fun(value)) // 循環執行then已插入的resolve方法
            }
    }
    
    // callback中執行的reject方法
    reject = (reason) => {
        // 判斷狀態是否須要是pending
            if (this.state === stateArr[0]) {
               this.state = stateArr[1]; // 更新狀態爲 fulfilled
               this.reason = reason; // 寫入最終的返回值
               
               this.rejectArr.forEach(fun => fun(reason)) // 循環執行then已插入的reject方法
            }
    }
    
    // then方法
    then = (onFulilled, onRejected) => {
        // 判斷onFulilled 和 onRejected是不是一個函數,若是不是函數則忽略它
        onFulilled = typeof onFulilled === 'function' ? onFulilled : (value) => value;
        onRejected = typeof onRejected === 'function' ? onRejected : (reason) => reason;

        // 若是狀態爲pending
        if (this.state === stateArr[0]) {
            return new MyPromise((resolve, reject) => {
                // 插入成功時調用的函數
                this.resolveArr.push((value) => {
                    try {
                        const result = onFulilled(value);
                        if (result instanceof MyPromise) {
                            result.then(resolve, reject);
                        } else {
                            resolve(result);
                        }
                    } catch(err) {
                        reject(err);
                    }
                })
                
                // 插入失敗時調用的函數
                this.rejectArr.push((value) => {
                    try {
                        const result = onRejected(value);
                        if (result instanceof MyPromise) {
                            result.then(resolve, reject);
                        } else {
                            resolve(result);
                        }
                    } catch(err) {
                        reject(err)
                    }
                })
            })
            
        }
        
        // 若是狀態是fulfilled
        if (this.state === stateArr[1]) {
            // then返回的必須是一個promise
            return new MyPromise((resolve, reject) => {
                try {
                    const result = onFulilled(this.value); // 執行傳入的onFulilled方法
                    
                    // 若是onFulilled返回的是一個Promise,則調用then方法
                    if (result instanceof MyPromise) {
                        result.then(resolve, reject);
                    } else {
                        resolve(result);
                    }
                } catch(err) {
                    reject(err);
                }
            })
        }
        
        // 若是狀態是rejected
        if (this.state === stateArr[2]) {
            // then返回的必須是一個promise
            return new MyPromise((resolve, reject) => {
                try {
                    const result = onRejected(this.reason); // 執行傳入的onRejected方法
                    
                    // 若是onRejected返回的是一個Promise,則調用then方法
                    if (result instanceof MyPromise) {
                        result.then(resolve, reject);
                    } else {
                        resolve(result);
                    }
                } catch(err) {
                    reject(err);
                }
            })
        }
    }

    // 調用then中的reject
    catch = (reject) => {
        this.then(null, reject);
    }
}

MyPromise.resolve = (value) => {
    return new MyPromise((resolve, reject) => { resolve(value) });
}

MyPromise.resolve = (reason) => {
    return new MyPromise((resolve, reject) => { reject(reason) });
}

複製代碼

小結

此次咱們瞭解了promise是如何實現的:

  • 必須是構造函數
  • 三種狀態(pending,resolve,reject)
  • then方法(promise中必需要有的方法)

從構造函數開始,到三種狀態的實現,最後實現then方法一步步根據Promise規則來實現Promise。瞭解完之後就能夠在面試官面前手寫一個Promise啦!😄

相關文章
相關標籤/搜索