手寫Promise

我們先定義一個Promise 類。

        const PENDING = 'PENDING';
        const FULFILLED = 'FULFILLED';
        const REJECTED = 'REJECTED';
        class Promise {
            constructor(executor) {
                this.status = PENDING;
                this.value = undefined;
                this.reason = undefined;
                let resolve = (value) => {
                    if (this.status === PENDING) {
                        this.status = FULFILLED;
                        this.value = value;
                    }
                }
                let reject = (reason) => {
                    if (this.status === PENDING) {
                        this.status = REJECT;
                        this.reason = reason;
                    }
                }
                try {
                    executor(resolve, reject)
                    //直接執(zhí)行excutor并傳入resolve和reject方法。
                } catch (err) {
                    reject(err)
                }
            }
}

我們來分析一下竿屹,首先executor(執(zhí)行器)是什么呢恒水?

let promise = new Promise((res,rej) => {
res('成功了')姿现;
})

那么這個時候executor就是:

(res,rej) => {
res('成功了');
}

之后在這個構造函數中又定義了status(狀態(tài)默認PENDING)、成功狀態(tài)數據:value(默認undefined)、失敗狀態(tài)的原因:reason(默認undefined)速警、以及resolve、reject方法鸯两,最后通過try直接執(zhí)行闷旧。
可以看到給excutor傳入了resolve,reject方法钧唐。
那么上面寫的:

(res,rej) => {
res('成功了')忙灼;
}

中的res,rej就是定義在構造函數上的方法。執(zhí)行res('成功了')该园,相當于執(zhí)行:

                let resolve = (value) => {
                    if (this.status === PENDING) {
                        this.status = FULFILLED;
                        this.value = value;
                    }
                }

并傳入‘成功了’為value的實參酸舍。
我們再加上then方法,

  then(onFulfilled, onRejected) {
    if (this.status === FULFILLED) {
      onFulfilled(this.value)
    }

    if (this.status === REJECTED) {
      onRejected(this.reason)
    }
  }
}

注意:這里的then不是constructor里面的里初,而是原型上的父腕。
不過這樣寫有一個問題。

        let promise = new Promise((res, rej) => {
            setTimeout(() => {
                res('成功了')
            }, 1000);
        }).then(res => {
            console.log(res)
        })//ReferenceError: PENDING is not defined

這樣寫會報錯青瀑,這是為什么呢?
因為setTimeout是異步操作萧诫,所以會先執(zhí)行then斥难,而這個時候由于還沒執(zhí)行resolve或reject方法,status仍為PENDING狀態(tài)帘饶,所以會報錯哑诊。
那我們改一下代碼。

        const PENDING = 'PENDING';
        const FULFILLED = 'FULFILLED';
        const REJECTED = 'REJECTED';
        class Promise {
            constructor(executor) {
                this.status = PENDING;
                this.value = undefined;
                this.reason = undefined;
                this.onResolvedCallbacks = [];
                this.onRejectedCallbacks = [];

                let resolve = (value) => {
                    if (this.status === PENDING) {
                        this.status = FULFILLED;
                        this.value = value;
                        this.onResolvedCallbacks.forEach(fn => fn());
                    }
                }
                let reject = (reason) => {
                    if (this.status === PENDING) {
                        this.status = REJECT;
                        this.reason = reason;
                        this.onRejectedCallbacks.forEach(fn => fn());
                    }
                }
                try {
                    executor(resolve, reject)
                        //直接執(zhí)行excutor并傳入resolve和reject方法及刻。
                } catch (err) {
                    reject(err)
                }
            }
            then(onFulfilled, onRejected) {
                if (this.status === FULFILLED) {
                    onFulfilled(this.value)
                }

                if (this.status === REJECTED) {
                    onRejected(this.reason)
                }
                if (this.status === PENDING) {
                    this.onResolvedCallbacks.push(() => {
                        onFulfilled(this.value)
                    });
                    this.onRejectedCallbacks.push(() => {
                        onRejected(this.reason);
                    })

                }

            }
        }

then方法接受兩個參數镀裤,我們給then方法執(zhí)行的的這兩個參數(也是方法)傳入this.value以及this.reason。并將這兩個方法分別保存onResolvedCallbacks和onRejectedCallbacks中缴饭。但定時器進入回調隊列并且主線程任務執(zhí)行完以后暑劝,調用resolve或reject方法,

                let resolve = (value) => {
                    if (this.status === PENDING) {
                        this.status = FULFILLED;
                        this.value = value;
                        this.onResolvedCallbacks.forEach(fn => fn());
                    }
                }
                let reject = (reason) => {
                    if (this.status === PENDING) {
                        this.status = REJECT;
                        this.reason = reason;
                        this.onRejectedCallbacks.forEach(fn => fn());
                    }
                }

可以看出來在調用onResolvedCallbacks或onRejectedCallbacks函數以前颗搂,就已經賦值了this.value和this.reason担猛。

        let promise = new Promise((res, rej) => {
            setTimeout(() => {
                res('成功了')
            }, 1000);
        }).then(res => {
            console.log(res)
        })//成功了

1秒后打印成功了。
哎呀丢氢,那這里為啥子要用forEach遍歷整個數組呢傅联?因為,要知道疚察,Promise使用then方法后會返回一個Promise對象蒸走。而這樣,就說明一個Promise可以調用多次then方法貌嫡,那每個then方法中如果都使用到異步比驻,那么forEach就顯得很有用了。后面再舉個例子哈衅枫。
我們知道then方法是可以不傳參數的嫁艇,那我們怎么進行數據的傳遞呢?
另外弦撩,

        let promise = new Promise((res,rej) => {
            res(10);
        });
        let test = promise.then(res => {
            return test//TypeError: Chaining cycle detected for promise #<Promise>
        })

這種當then的返回值與本身相同的時候步咪,怎么解決?
先改一下then方法益楼。

then(onFulfilled, onRejected) {
         onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : v=>v;
         onRejected = typeof onRejected === 'function' ? onRejected : err => {
                    throw err
                };//通過這兩行代碼解決then方法不傳入參數的問題猾漫。
                let promise2 = new Promise((res, rej) => {
                    if (this.status === FULFILLED) {
                        setTimeout(() => {
                            try {
                                let x = onFulfilled(this.value);
                                resolvePromise(promise2, x, res, rej)
                            } catch (e) {
                                reject(e)

                            }
                        }, 0);
                    }

                    if (this.status === REJECT) {
                        setTimeout(() => {
                            try {
                                let x = onRejected(this.reason);
                                resolvePromise(promise2, x, res, rej)
                            } catch (e) {
                                reject(e)

                            }
                        }, 0);
                    }

                    if (this.status === PENDING) {
                        this.onResolvedCallbacks.push(() => {
                            setTimeout(() => {
                                try {
                                    let x = onFulfilled(this.value);
                                    resolvePromise(promise2, x, res, rej)
                                } catch (e) {
                                    reject(e)
                                }
                            }, 0);
                        });
                        this.onRejectedCallbacks.push(() => {
                            setTimeout(() => {
                                try {
                                    let x = onRejected(this.value);
                                    resolvePromise(promise2, x, res, rej)
                                } catch (e) {
                                    reject(e)
                                }
                            }, 0);
                        })

                    }
                });
                return promise2
            }
        }

resolvePromise(promise2, x, res, rej)方法用于解決單then的返回值與本身相同的問題点晴,以及其他返回值問題例如,返回簡單數據類型悯周,function粒督,其他的promise,而其他的promise中又有promise等問題禽翼。

前面也說了定時器是異步的屠橄,三個if中都借用異步,為的是先返回promise2闰挡,在執(zhí)行定時器里的代碼锐墙,不這么用,promise2都還沒執(zhí)行完呢长酗。

那么這樣就簡單了溪北,promise2是then的返回值,x可以使任意值夺脾。
再來定義resolvePromise

//這是定義在全局作用域中的
 const resolvePromise = (promise2, x, resolve, reject) => {
            // 自己等待自己完成是錯誤的實現(xiàn)之拨,用一個類型錯誤,結束掉 
            if (promise2 === x) {
                return reject(new TypeError('Chaining cycle detected for promise #<Promise>'))
            }
            let called;
            // 后續(xù)的條件要嚴格判斷 保證代碼能和別的庫一起使用
            if ((typeof x === 'object' && x != null) || typeof x === 'function') {
                try {
                    let then = x.then;
                    if (typeof then === 'function') {
                        then.call(x, y => { // 根據 promise 的狀態(tài)決定是成功還是失敗
                            if (called) return;
                            called = true;
                            // 遞歸解析的過程(因為可能 promise 中還有 promise) Promise/A+ 2.3.3.3.1
                            resolvePromise(promise2, y, resolve, reject);
                        }, r => {
                            // 只要失敗就失敗 Promise/A+ 2.3.3.3.2
                            if (called) return;
                            called = true;
                            reject(r);
                        });
                    } else {
                        // 如果 x.then 是個普通值就直接返回 resolve 作為結果  Promise/A+ 2.3.3.4
                        resolve(x);
                    }
                } catch (e) {
                    // Promise/A+ 2.3.3.2
                    if (called) return;
                    called = true;
                    reject(e)
                }
            } else {
                // 如果 x 是個普通值就直接返回 resolve 作為結果  Promise/A+ 2.3.4  
                resolve(x)
            }
        }

一步一步來咧叭,

 if (promise2 === x) {
                return reject(new TypeError('Chaining cycle detected for promise #<Promise>'))
            }

這一步就是解決then return 自己的時候的問題蚀乔。
后面的代碼就有長了,首先判斷是不是復雜(引用數據類型)菲茬,

if ((typeof x === 'object' && x != null) || typeof x === 'function')

不是的話就說明是簡單數據類型乙墙,直接就:

 else {
                // 如果 x 是個普通值就直接返回 resolve 作為結果  
                resolve(x)
            }

再判斷這個復雜類型有沒有then方法

   if (typeof then === 'function') 

結果為true的話就說明這個x是Promise對象,否則就是簡單數據類型生均,直接resolve

 else {
  // 如果 x.then 是個普通值就直接返回 resolve 作為結果 
                        resolve(x);
                    }

而最中間的代碼听想,

then.call(x, y => { // 根據 promise 的狀態(tài)決定是成功還是失敗
                            if (called) return;
                            called = true;
                            // 遞歸解析的過程(因為可能 promise 中還有 promise) Promise/A+ 2.3.3.3.1
                            resolvePromise(promise2, y, resolve, reject);
                        }, r => {
                            // 只要失敗就失敗 Promise/A+ 2.3.3.3.2
                            if (called) return;
                            called = true;
                            reject(r);
                        });

就像注釋的那樣。
而之前說的為什么要遍歷onResolvedCallbacks或onRejectedCallbacks是因為马胧,我可能異步操作promise中的reject或resolve汉买,并在后面調用then方法,而在這個then方法中return出來一個新的Promise佩脊,再次借助定時器使得這個promise狀態(tài)也為PENDING以此類推蛙粘。

        const promise = new Promise((resolve, reject) => {
                setTimeout(() => {
                    reject('失敗');
                }, 1000)
            }).then(null, err => {
                return new Promise((res, rej) => {
                    setTimeout(() => {
                        console.log(err);
                        rej('失敗1');
                    }, 2000)
                })
            }).then(null, err => {
                return new Promise((res, rej) => {
                    setTimeout(() => {
                        console.log(err);
                        rej('失敗2');
                    }, 3000)
                })
            }).then(data => {
                console.log(data);
            }, err => {
                console.log('err', err);
            })

而現(xiàn)在的整個代碼為:

  const PENDING = 'PENDING';
        const FULFILLED = 'FULFILLED';
        const REJECTED = 'REJECTED';

        const resolvePromise = (promise2, x, resolve, reject) => {
            // 自己等待自己完成是錯誤的實現(xiàn),用一個類型錯誤威彰,結束掉 promise  Promise/A+ 2.3.1
            if (promise2 === x) {
                return reject(new TypeError('Chaining cycle detected for promise #<Promise>'))
            }
            // Promise/A+ 2.3.3.3.3 只能調用一次
            let called;
            // 后續(xù)的條件要嚴格判斷 保證代碼能和別的庫一起使用
            if ((typeof x === 'object' && x != null) || typeof x === 'function') {
                try {
                    // 為了判斷 resolve 過的就不用再 reject 了(比如 reject 和 resolve 同時調用的時候)  Promise/A+ 2.3.3.1
                    let then = x.then;
                    if (typeof then === 'function') {
                        // 不要寫成 x.then出牧,直接 then.call 就可以了 因為 x.then 會再次取值,Object.defineProperty  Promise/A+ 2.3.3.3
                        then.call(x, y => { // 根據 promise 的狀態(tài)決定是成功還是失敗
                            if (called) return;
                            called = true;
                            // 遞歸解析的過程(因為可能 promise 中還有 promise) Promise/A+ 2.3.3.3.1
                            resolvePromise(promise2, y, resolve, reject);
                        }, r => {
                            // 只要失敗就失敗 Promise/A+ 2.3.3.3.2
                            if (called) return;
                            called = true;
                            reject(r);
                        });
                    } else {
                        // 如果 x.then 是個普通值就直接返回 resolve 作為結果  Promise/A+ 2.3.3.4
                        resolve(x);
                    }
                } catch (e) {
                    // Promise/A+ 2.3.3.2
                    if (called) return;
                    called = true;
                    reject(e)
                }
            } else {
                // 如果 x 是個普通值就直接返回 resolve 作為結果  Promise/A+ 2.3.4  
                resolve(x)
            }
        }

        class Promise {
            constructor(executor) {
                this.status = PENDING;
                this.value = undefined;
                this.reason = undefined;
                this.onResolvedCallbacks = [];
                this.onRejectedCallbacks = [];

                let resolve = (value) => {
                    if (this.status === PENDING) {
                        this.status = FULFILLED;
                        this.value = value;
                        this.onResolvedCallbacks.forEach(fn => fn());
                    }
                }

                let reject = (reason) => {
                    if (this.status === PENDING) {
                        this.status = REJECTED;
                        this.reason = reason;
                        this.onRejectedCallbacks.forEach(fn => fn());
                    }
                }

                try {
                    executor(resolve, reject)
                } catch (error) {
                    reject(error)
                }
            }

            then(onFulfilled, onRejected) {
                //解決 onFufilled歇盼,onRejected 沒有傳值的問題
                //Promise/A+ 2.2.1 / Promise/A+ 2.2.5 / Promise/A+ 2.2.7.3 / Promise/A+ 2.2.7.4
                onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : v => v;
                //因為錯誤的值要讓后面訪問到舔痕,所以這里也要跑出個錯誤,不然會在之后 then 的 resolve 中捕獲
                onRejected = typeof onRejected === 'function' ? onRejected : err => {
                    throw err
                };
                // 每次調用 then 都返回一個新的 promise  Promise/A+ 2.2.7
                let promise2 = new Promise((resolve, reject) => {
                    if (this.status === FULFILLED) {
                        //Promise/A+ 2.2.2
                        //Promise/A+ 2.2.4 --- setTimeout
                        setTimeout(() => {
                            try {
                                //Promise/A+ 2.2.7.1
                                let x = onFulfilled(this.value);
                                // x可能是一個proimise
                                resolvePromise(promise2, x, resolve, reject);
                            } catch (e) {
                                //Promise/A+ 2.2.7.2
                                reject(e)
                            }
                        }, 0);
                    }

                    if (this.status === REJECTED) {
                        //Promise/A+ 2.2.3
                        setTimeout(() => {
                            try {
                                let x = onRejected(this.reason);
                                resolvePromise(promise2, x, resolve, reject);
                            } catch (e) {
                                reject(e)
                            }
                        }, 0);
                    }

                    if (this.status === PENDING) {
                        this.onResolvedCallbacks.push(() => {
                            setTimeout(() => {
                                try {
                                    let x = onFulfilled(this.value);
                                    resolvePromise(promise2, x, resolve, reject);
                                } catch (e) {
                                    reject(e)
                                }
                            }, 0);
                        });

                        this.onRejectedCallbacks.push(() => {
                            setTimeout(() => {
                                try {

                                    let x = onRejected(this.reason);
                                    resolvePromise(promise2, x, resolve, reject)
                                } catch (e) {
                                    reject(e)
                                }
                            }, 0);
                        });
                    }
                });

                return promise2;
            }
        }
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市伯复,隨后出現(xiàn)的幾起案子慨代,更是在濱河造成了極大的恐慌,老刑警劉巖啸如,帶你破解...
    沈念sama閱讀 217,826評論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件侍匙,死亡現(xiàn)場離奇詭異,居然都是意外死亡叮雳,警方通過查閱死者的電腦和手機想暗,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,968評論 3 395
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來帘不,“玉大人江滨,你說我怎么就攤上這事⊙峋” “怎么了?”我有些...
    開封第一講書人閱讀 164,234評論 0 354
  • 文/不壞的土叔 我叫張陵告唆,是天一觀的道長棺弊。 經常有香客問我,道長擒悬,這世上最難降的妖魔是什么模她? 我笑而不...
    開封第一講書人閱讀 58,562評論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮懂牧,結果婚禮上侈净,老公的妹妹穿的比我還像新娘。我一直安慰自己僧凤,他們只是感情好畜侦,可當我...
    茶點故事閱讀 67,611評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著躯保,像睡著了一般旋膳。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上途事,一...
    開封第一講書人閱讀 51,482評論 1 302
  • 那天验懊,我揣著相機與錄音,去河邊找鬼尸变。 笑死义图,一個胖子當著我的面吹牛,可吹牛的內容都是我干的召烂。 我是一名探鬼主播碱工,決...
    沈念sama閱讀 40,271評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了痛垛?” 一聲冷哼從身側響起草慧,我...
    開封第一講書人閱讀 39,166評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎匙头,沒想到半個月后漫谷,有當地人在樹林里發(fā)現(xiàn)了一具尸體,經...
    沈念sama閱讀 45,608評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡蹂析,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 37,814評論 3 336
  • 正文 我和宋清朗相戀三年舔示,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片电抚。...
    茶點故事閱讀 39,926評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡惕稻,死狀恐怖,靈堂內的尸體忽然破棺而出蝙叛,到底是詐尸還是另有隱情俺祠,我是刑警寧澤,帶...
    沈念sama閱讀 35,644評論 5 346
  • 正文 年R本政府宣布借帘,位于F島的核電站蜘渣,受9級特大地震影響,放射性物質發(fā)生泄漏肺然。R本人自食惡果不足惜蔫缸,卻給世界環(huán)境...
    茶點故事閱讀 41,249評論 3 329
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望际起。 院中可真熱鬧拾碌,春花似錦、人聲如沸街望。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,866評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽灾前。三九已至展融,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間豫柬,已是汗流浹背告希。 一陣腳步聲響...
    開封第一講書人閱讀 32,991評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留烧给,地道東北人燕偶。 一個月前我還...
    沈念sama閱讀 48,063評論 3 370
  • 正文 我出身青樓,卻偏偏與公主長得像础嫡,于是被迫代替她去往敵國和親指么。 傳聞我的和親對象是個殘疾皇子酝惧,可洞房花燭夜當晚...
    茶點故事閱讀 44,871評論 2 354

推薦閱讀更多精彩內容

  • 1. promise要解決的問題: 腦筋急轉彎:把牛關進冰箱里,要分幾步伯诬? 很顯然晚唇,這三個操作不能顛倒順序,否則任...
    月上秦少閱讀 1,589評論 0 3
  • 原文詳見:Promise實現(xiàn)原理(附源碼)參考文章:BAT前端經典面試問題:史上最最最詳細的手寫Promise教程...
    張小明_to閱讀 107評論 0 1
  • 前言 都 2020 年了盗似,Promise 大家肯定都在用了哩陕,但是估計很多人對其原理還是一知半解,今天就讓我們一起實...
    superYuee閱讀 1,350評論 1 7
  • #### 手寫Promise源碼 // promise有三種狀態(tài) pending 等待 fulfilled 成功 ...
    爵跡01閱讀 248評論 0 1
  • 黑色的海島上懸著一輪又大又圓的明月,毫不嫌棄地把溫柔的月色照在這寸草不生的小島上接癌。一個少年白衣白發(fā)心赶,悠閑自如地倚坐...
    小水Vivian閱讀 3,108評論 1 5