ES6-Promise 原理分析

promise是什麼

以下來自百度
所謂Promise,簡單說就是一個容器,裏面保存着某個未來纔會結束的事件(通常是一個異步操作)的結果。從語法上說,Promise 是一個對象,從它可以獲取異步操作的消息。Promise 提供統一的 API,各種異步操作都可以用同樣的方法進行處理。

Promise對象有以下兩個特點。
(1)對象的狀態不受外界影響。Promise對象代表一個異步操作,有三種狀態:Pending(進行中)、Resolved(已完成,又稱 Fulfilled)和Rejected(已失敗)。只有異步操作的結果,可以決定當前是哪一種狀態,任何其他操作都無法改變這個狀態。這也是Promise這個名字的由來,它的英語意思就是“承諾”,表示其他手段無法改變。
(2)一旦狀態改變,就不會再變,任何時候都可以得到這個結果。Promise對象的狀態改變,只有兩種可能:從Pending變爲Resolved和從Pending變爲Rejected。只要這兩種情況發生,狀態就凝固了,不會再變了,會一直保持這個結果。就算改變已經發生了,你再對Promise對象添加回調函數,也會立即得到這個結果。這與事件(Event)完全不同,事件的特點是,如果你錯過了它,再去監聽,是得不到結果的。
有了Promise對象,就可以將異步操作以同步操作的流程表達出來,避免了層層嵌套的回調函數。此外,Promise對象提供統一的接口,使得控制異步操作更加容易。

promise圖解

promise有兩大狀態,未決狀態和已決狀態

  1. 未決狀態:PENDING
  2. 已決狀態:RESOLVED/REJECTED

promise中有兩個方法可以將未決狀態推向已決狀態

  1. resolve:將狀態從PENDING推向RESOLVED
  2. reject:將狀態從PENDING推向REJECTED

promise還有兩個列表用來存儲成功後的回調函數與失敗的回調函數
thenables/catchables

圖解如下
在這裏插入圖片描述

手動封裝MyPromise

既然明白了Promise的原理,那麼promise內部就要有4個變量來存儲相應的狀態

在這裏封裝的MyPromise沒有添加鏈式調用的功能,因爲鏈式調用還沒有搞懂。。。如果你明白鏈式調用的原理,請聯繫15011177648(微信同號)

  1. 當前狀態
  2. 值(後續處理函數的參數)
  3. 成功狀態回調列表
  4. 失敗狀態回調列表
class MyPromise {
    // 接受一個函數
    constructor(fun) {
        // 定義後續處理參數
        this.value = undefined;
        // 定義promise狀態,最初是未決
        this.status = "PENDDING";
        // 定義成功後續處理函數列表
        this.thenableList = [];
        // 定義失敗後續處理函數列表
        this.catchableList = [];
        // 將狀態推向成功的函數
        let resolve = (value) => {
            this.changeStatus('RESOLVED', value);
            // 循環執行成功隊列的函數
            this.thenableList.forEach((item) => {
                item(this.value)
            })
        }
        // 將狀態推向失敗的函數
        let reject = (value) => {
            this.changeStatus('REJECTED', value);
            // 循環執行失敗隊列的函數
            this.catchableList.forEach((item) => {
                item(this.value)
            })
        }
        // 嘗試執行傳進來的函數
        try {
            fun(resolve, reject);
        } catch (e) {
            reject(e)
        }
    }
    // 改變狀態
    changeStatus(newStatus, value) {
        // 由於狀態是不可逆的,所以先盤算是不是未決,不是直接返回
        if (this.status != "PENDDING") return
        this.status = newStatus;
        this.value = value
    }
    // 後續處理
    settleHandle(newStatus, able, list) {
        // 判斷參數是否爲函數
        if (typeof able != 'function') {
            return
        }
        // 判斷狀態
        if (this.status == newStatus) {
            // 在Promise中,後續處理函數應該是放在事件微隊列,但是jsAPI無法將事件放入微隊列,這裏使用setTimeout的宏隊列代替
            // 如果已經是已決了,直接執行
            setTimeout(() => {
                able(this.value)
            }, 0)
        } else {
            // 不是已決的話就放進任務隊列,等待狀態改變
            list.push(able);
        }
    }
    // 添加成功處理函數
    then(thenable, catchable) {
        this.settleHandle('RESOLVED', thenable, this.thenableList);
        // 由於成功處理函數可接受兩個參數,所以這裏也要調一下失敗處理函數
        this.catch(catchable)
    }
    // 添加失敗處理函數
    catch(catchable) {
        this.settleHandle('REJECTED', catchable, this.catchableList)
    }
}

私有化變量

在上面的promise中,完成了promise的基本功能,但是真正的Promise的某些變量,外接是訪問不到的(除了then,catch,finally都訪問不到),所以我們使用閉包加符號來私有化變量。

有關符號的使用,我會再寫一篇博客來介紹

const MyPromise = (() => {
    const PENDING = 'pending',
        RESOLVED = 'resolved',
        REJECTED = 'rejected',
        promiseValue = Symbol('後續處理參數'),
        promiseStatus = Symbol('當前狀態'),
        thenables = Symbol('成功後的調用函數列表'),
        catchables = Symbol('成功後的調用函數列表'),
        changeStatus = Symbol('改變狀態'),
        settleHandle = Symbol('後續處理');
    return class MyPromise {
        // 新狀態, then的參數
        [changeStatus](newStatus, result, queue) {
            if (this[promiseStatus] !== PENDING) {
                //狀態無法變更
                return;
            }
            this[promiseStatus] = newStatus;
            this[promiseValue] = result;
            queue.forEach(handle => {
                handle(this[promiseValue])
            });
        }
        constructor(executor) {
            this[promiseValue] = undefined;
            this[promiseStatus] = PENDING;

            this[thenables] = [];
            this[catchables] = [];

            const resolve = (result) => {
                this[changeStatus](RESOLVED, result, this[thenables]);
            }

            const reject = (err) => {
                this[changeStatus](REJECTED, err, this[catchables]);
            }

            try {
                executor(resolve, reject)
            } catch (err) {
                reject(err)
            }
        }
        // 後續事件, 立即執行狀態, 後續事件列表
        [settleHandle](handle, status, queueList) {
            if (typeof handle !== 'function') {
                return
            }
            if (this[promiseStatus] == status) {
                setTimeout(() => {
                    handle(this[promiseValue])
                }, 0)
            } else {
                queueList.push(handle);
            }
        }
        then(thenable, catchable) {

            this[settleHandle](thenable, RESOLVED, this[thenables])
            this.catch(catchable)
        }
        catch(catchable) {
            this[settleHandle](catchable, REJECTED, this[catchables])
        }
    }
})()

Promise的靜態方法

promise還有一些靜態方法(這些靜態方法怎麼用就不做贅述了)

  1. all
  2. rece
  3. resolve
  4. reject

現在我們來實現一下這四個靜態方法

const MyPromise = (() => {
    const PENDING = 'pending',
        RESOLVED = 'resolved',
        REJECTED = 'rejected',
        promiseValue = Symbol('後續處理參數'),
        promiseStatus = Symbol('當前狀態'),
        thenables = Symbol('成功後的調用函數列表'),
        catchables = Symbol('成功後的調用函數列表'),
        changeStatus = Symbol('改變狀態'),
        settleHandle = Symbol('後續處理');
    return class MyPromise {
        // ...(略)
        // 靜態方法-resolve
        static resolve(data) {
            if (data instanceof MyPromise) {
                return data
            } else {
                return MyPromise(resolve => {
                    resolve(data)
                })
            }
        }
        // 靜態方法-reject
        static reject(reason) {
            return MyPromise((resolve, reject) => {
                reject(reason)
            })
        }
        // 靜態方法-all
        static all(ProList) {
            return new MyPromise((resolve, reject) => {
                // 給每個promise對象註冊後續處理
                // 成功就查看是否全部成功,全部成功讓all的promise也成功
                // 失敗就直接失敗
                let result = ProList.map((p) => {
                    let obj = {
                        value: undefined,
                        isResolved: false
                    }
                    p.then((data) => {
                        obj.value = data;
                        obj.isResolved = true;
                        // then的後續處理函數是放在微隊列,所以當後續處理函數執行時result已經有值了
                        let filterProList = result.filter((item) => !item.isResolved);
                        if (filterProList.length == 0) {
                            resolve(result.map(r => r.value))
                        }
                    }, (reason) => {
                        reject(reason)
                    })
                    return obj
                })
            })
        }
        // 靜態方法-race
        static race(ProList) {
            // 給每個promise註冊後續處理函數,變爲已決就將rece的promise推向對應狀態
            return new MyPromise((resolve, reject) => {
                ProList.forEach((p) => {
                    p.then(data => { 
                        resolve(data) 
                    },err => { 
                        reject(err) 
                    })
                })
            })
        }
    }
})()
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章