手寫(xiě)極簡(jiǎn)版promise

為了更更加好的理解promise ,簡(jiǎn)單的學(xué)習(xí)了一下手寫(xiě)promise。
于是我就簡(jiǎn)單的學(xué)了一下 腔呜,首先分步操作,
先實(shí)現(xiàn)基礎(chǔ)部分再悼,不考慮鏈?zhǔn)秸{(diào)用核畴,

function myPromise(executor) {
    var _this = this;
    this.state = 'pending';
    this.value = undefined;
    this.reason = undefined;
    this.onFulfilledFunc = [];//保存成功回調(diào)
    this.onRejectedFunc = [];//保存失敗回調(diào)
    //其它代碼略...
 function resolve(value) {
        //當(dāng)狀態(tài)為pending時(shí)再做更新
        if (_this.state === 'pending') {
            _this.value = value;//保存成功結(jié)果
            _this.state = 'resolved';
        }

    }

    function reject(reason) {
    //當(dāng)狀態(tài)為pending時(shí)再做更新
        if (_this.state === 'pending') {
            _this.reason = reason;//保存失敗原因
            _this.state = 'rejected';
        }
    }
//這里執(zhí)行promise傳入的函數(shù)部分
try {
        executor(resolve, reject); //馬上執(zhí)行
            } 
catch (error) {
                reject(error)
            }
}

then方法的實(shí)現(xiàn)
當(dāng)myPromise的狀態(tài)發(fā)生了改變,不論是成功或是失敗都會(huì)調(diào)用then方法冲九,所以谤草,then方法的實(shí)現(xiàn)也很簡(jiǎn)單,根據(jù)state狀態(tài)來(lái)調(diào)用不同的回調(diào)函數(shù)即可,但是由于執(zhí)行的時(shí)候可能狀態(tài)還是pending 所以執(zhí)行的時(shí)候如果是pending 我們則把傳入的成功和失敗的回調(diào)函數(shù)傳入callback數(shù)組里面 等resolve函數(shù)里面去執(zhí)行

myPromise.prototype.then = function (onFulfilled, onRejected) {
    //等待態(tài)莺奸,此時(shí)異步代碼還沒(méi)有走完
    if (this.state === 'pending') {
        if (typeof onFulfilled === 'function') {
            this.onFulfilledFunc.push(onFulfilled);//保存回調(diào)
        }
        if (typeof onRejected === 'function') {
            this.onRejectedFunc.push(onRejected);//保存回調(diào)
        }
    }
    //其它代碼略...
}

鏈?zhǔn)秸{(diào)用
romise處理異步代碼最強(qiáng)大的地方就是支持鏈?zhǔn)秸{(diào)用丑孩,這塊也是最復(fù)雜的,我們先梳理一下規(guī)范中是怎么定義的:
每個(gè)then方法都返回一個(gè)新的Promise對(duì)象(原理的核心)
如果then方法中顯示地返回了一個(gè)Promise對(duì)象就以此對(duì)象為準(zhǔn)灭贷,返回它的結(jié)果
如果then方法中返回的是一個(gè)普通值(如Number温学、String等)就使用此值包裝成一個(gè)新的Promise對(duì)象返回。
如果then方法中沒(méi)有return語(yǔ)句甚疟,就視為返回一個(gè)用Undefined包裝的Promise對(duì)象
若then方法中出現(xiàn)異常仗岖,則調(diào)用失敗態(tài)方法(reject)跳轉(zhuǎn)到下一個(gè)then的onRejected
如果then方法沒(méi)有傳入任何回調(diào),則繼續(xù)向下傳遞(值的傳遞特性)古拴。
看起來(lái)抽象 代碼解釋看下面簡(jiǎn)書(shū)博客
resolve 和reject的改造:
對(duì)于 resolve 函數(shù)來(lái)說(shuō)箩帚,首先需要判斷傳入的值是否為 Promise 類(lèi)型
為了保證函數(shù)執(zhí)行順序,需要將兩個(gè)函數(shù)體代碼使用 setTimeout 包裹起來(lái)

function resolve(value) {
                // 對(duì)于 resolve 函數(shù)來(lái)說(shuō)黄痪,首先需要判斷傳入的值是否為 Promise 類(lèi)型
                if (value instanceof myPromise) {
                    return value.then(resolve, reject)
                }
                setTimeout(() => {
                    if (_this.state = 'pending') {
                        _this.value = value;
                        _this.onFullfiledCallBack.forEach(fn => fn(value));
                        _this.state = 'resolved';
                    }
                }, 0)

            }
            function reject(reason) {
                setTimeout(() => {
                    if (_this.state = 'pending') {
                        _this.reason = reason;
                        _this.onRejectCallBack.forEach(fn => fn(value));
                        _this.state = 'rejected';
                    }
                }, 0)

            }

接下來(lái)改造的then代碼:

myPromise.prototype.then = function (onFullfiled, onRejected) {
            let self = this;
            let promise2 = null;
            onFullfiled = typeof onFullfiled === 'function' ? onFullfiled : v => v;
            onRejected = typeof onRejected === 'function' ? onRejected : r => { throw r };
            if (this.state == 'pending') {
                return (promise2 = new myPromise((resolve, reject) => {
                    self.onFullfiledCallBack.push(() => {
                        try {
                            let x = onFullfiled(self.value);
                            //處理then不同的返回值和下面?zhèn)鬟f鏈的傳遞方式
                            resolutionProduce(promise2, x, resolve, reject);
                        } catch (e) {
                            reject(e)
                        }
                    });
                    self.onRejectCallBack.push(() => {
                        try {
                            let x = onRejected(self.value);
                            resolutionProduce(promise2, x, resolve, reject);
                        } catch (e) {
                            reject(e)
                        }
                    })
                }))
            }
        }

思路跟之前基本一致紧帕,只是說(shuō)把之前返回值改成promise,同時(shí)捕獲異常,在status狀態(tài)為FULFILLED或者REJECTED的時(shí)候執(zhí)行得加上異步setTimeout包裹。 接下來(lái)完成最核心的resolutionProduce函數(shù):

function resolutionProduce(promise, x, resolve, reject) {
            if (promise === x) {
                reject(new TypeError('error'))
            }
            if (x !== null && (typeof x === 'object' || typeof x === 'function')) {
                // 可能是個(gè)對(duì)象或者函數(shù)
                try {
                    let then = x.then;
                    if (typeof then === 'function') {
                        //取出的then是函數(shù)是嗜,那么執(zhí)行函數(shù) 愈案,就將x作為函數(shù)的作用域 this 調(diào)用之,并且傳遞兩個(gè)回調(diào)函數(shù)作為參數(shù)鹅搪,第一個(gè)參數(shù)叫做 resolvePromise 站绪,第二個(gè)參數(shù)叫做 rejectPromise
                        let y = then.call(x, y => {
                            //遞歸調(diào)用
                            resolutionProduce(promise, y, resolve, reject);
                        }, r => reject(r))
                    } else {
                        resolve(x);
                    }
                } catch (error) {
                    reject(error)
                }
            } else {
                //可能是普通值
                resolve(x); //把第一個(gè)then的返回值傳遞給返回的promise值里面的then函數(shù)
            }

        }

然后判斷 x 是否為對(duì)象或者函數(shù),如果都不是的話(huà)丽柿,將 x 傳入 resolve 中
如果 x 是對(duì)象或者函數(shù)的話(huà)恢准,先把 x.then 賦值給 then,然后判斷 then 的類(lèi)型甫题,如果不是函數(shù)類(lèi)型的話(huà)馁筐,就將 x 傳入 resolve 中
如果 then 是函數(shù)類(lèi)型的話(huà),就將 x 作為函數(shù)的作用域 this 調(diào)用之坠非,并且傳遞兩個(gè)回調(diào)函數(shù)作為參數(shù)敏沉,第一個(gè)參數(shù)叫做 resolvePromise ,第二個(gè)參數(shù)叫做 rejectPromise炎码,如果Promise對(duì)象轉(zhuǎn)為成功態(tài)或是失敗時(shí)傳入的還是一個(gè)Promise對(duì)象盟迟,此時(shí)應(yīng)該繼續(xù)執(zhí)行,直到最后的Promise執(zhí)行完潦闲。所以遞歸操作(這一點(diǎn)我有點(diǎn)繞)
以上代碼在執(zhí)行的過(guò)程中如果拋錯(cuò)了攒菠,將錯(cuò)誤傳入 reject 函數(shù)中

最后極簡(jiǎn)版手寫(xiě)promise就完成了
最后使用diamante驗(yàn)證:

let p = new myPromise((resolve, reject) => {
            setTimeout(() => {
                resolve('1')
            }, 1000)
        })
        p.then(data =>{
            console.log(data)
            return new myPromise((resolve,reject)=>{
                setTimeout(()=>{
                    console.log('我是then里面打印出來(lái)的');
                    resolve('2222')
                },1000)
            })
        } ).then(data=>console.log(data));

能正確打印 結(jié)束!
最終完整代碼:

function myPromise(executor) {
            let _this = this;
            this.state = 'pending';
            this.value = undefined;
            this.reason = undefined;
            this.onFullfiledCallBack = [];
            this.onRejectCallBack = [];

            function resolve(value) {
                // 對(duì)于 resolve 函數(shù)來(lái)說(shuō)矫钓,首先需要判斷傳入的值是否為 Promise 類(lèi)型
                if (value instanceof myPromise) {
                    return value.then(resolve, reject)
                }
                setTimeout(() => {
                    if (_this.state = 'pending') {
                        _this.value = value;
                        _this.onFullfiledCallBack.forEach(fn => fn(value));
                        _this.state = 'resolved';
                    }
                }, 0)

            }
            function reject(reason) {
                setTimeout(() => {
                    if (_this.state = 'pending') {
                        _this.reason = reason;
                        _this.onRejectCallBack.forEach(fn => fn(value));
                        _this.state = 'rejected';
                    }
                }, 0)

            }
            try {
                executor(resolve, reject); //馬上執(zhí)行
            } catch (error) {
                reject(error)
            }
        }
        myPromise.prototype.then = function (onFullfiled, onRejected) {
            let self = this;
            let promise2 = null;
            onFullfiled = typeof onFullfiled === 'function' ? onFullfiled : v => v;
            onRejected = typeof onRejected === 'function' ? onRejected : r => { throw r };
            if (this.state == 'pending') {
                return (promise2 = new myPromise((resolve, reject) => {
                    self.onFullfiledCallBack.push(() => {
                        try {
                            let x = onFullfiled(self.value);
                            //處理then不同的返回值和下面?zhèn)鬟f鏈的傳遞方式
                            resolutionProduce(promise2, x, resolve, reject);
                        } catch (e) {
                            reject(e)
                        }
                    });
                    self.onRejectCallBack.push(() => {
                        try {
                            let x = onRejected(self.value);
                            resolutionProduce(promise2, x, resolve, reject);
                        } catch (e) {
                            reject(e)
                        }
                    })
                }))
            }
        }
        //處理then返回值如何傳遞和下一個(gè)then的值
        function resolutionProduce(promise, x, resolve, reject) {
            if (promise === x) {
                reject(new TypeError('error'))
            }
            if (x !== null && (typeof x === 'object' || typeof x === 'function')) {
                // 可能是個(gè)對(duì)象或者函數(shù)
                try {
                    let then = x.then;
                    if (typeof then === 'function') {
                        //取出的then是函數(shù)要尔,那么執(zhí)行函數(shù) ,就將x作為函數(shù)的作用域 this 調(diào)用之新娜,并且傳遞兩個(gè)回調(diào)函數(shù)作為參數(shù)赵辕,第一個(gè)參數(shù)叫做 resolvePromise ,第二個(gè)參數(shù)叫做 rejectPromise
                        let y = then.call(x, y => {
                            //遞歸調(diào)用
                            resolutionProduce(promise, y, resolve, reject);
                        }, r => reject(r))
                    } else {
                        resolve(x);
                    }
                } catch (error) {
                    reject(error)
                }
            } else {
                //可能是普通值
                resolve(x); //把第一個(gè)then的返回值傳遞給返回的promise值里面的then函數(shù)
            }

        }
        let p = new myPromise((resolve, reject) => {
            setTimeout(() => {
                resolve('1')
            }, 1000)
        })
        p.then(data =>{
            console.log(data)
            return new myPromise((resolve,reject)=>{
                setTimeout(()=>{
                    console.log('我是then里面打印出來(lái)的');
                    resolve('2222')
                },1000)
            })
        } ).then(data=>console.log(data));

主要還是參考了幾篇文章:
http://www.reibang.com/p/c633a22f9e8c
https://blog.csdn.net/weixin_34348111/article/details/91374448
https://segmentfault.com/a/1190000020505870

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末概龄,一起剝皮案震驚了整個(gè)濱河市还惠,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌私杜,老刑警劉巖蚕键,帶你破解...
    沈念sama閱讀 217,657評(píng)論 6 505
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異衰粹,居然都是意外死亡锣光,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,889評(píng)論 3 394
  • 文/潘曉璐 我一進(jìn)店門(mén)铝耻,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)誊爹,“玉大人蹬刷,你說(shuō)我怎么就攤上這事∑登穑” “怎么了办成?”我有些...
    開(kāi)封第一講書(shū)人閱讀 164,057評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵,是天一觀(guān)的道長(zhǎng)搂漠。 經(jīng)常有香客問(wèn)我迂卢,道長(zhǎng),這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,509評(píng)論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮乌庶,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘拍摇。我一直安慰自己,他們只是感情好馆截,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,562評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著蜂莉,像睡著了一般蜡娶。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上映穗,一...
    開(kāi)封第一講書(shū)人閱讀 51,443評(píng)論 1 302
  • 那天窖张,我揣著相機(jī)與錄音,去河邊找鬼蚁滋。 笑死宿接,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的辕录。 我是一名探鬼主播睦霎,決...
    沈念sama閱讀 40,251評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼走诞!你這毒婦竟也來(lái)了副女?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 39,129評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤蚣旱,失蹤者是張志新(化名)和其女友劉穎碑幅,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體塞绿,經(jīng)...
    沈念sama閱讀 45,561評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡沟涨,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,779評(píng)論 3 335
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了异吻。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片裹赴。...
    茶點(diǎn)故事閱讀 39,902評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出篮昧,到底是詐尸還是另有隱情赋荆,我是刑警寧澤,帶...
    沈念sama閱讀 35,621評(píng)論 5 345
  • 正文 年R本政府宣布懊昨,位于F島的核電站窄潭,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏酵颁。R本人自食惡果不足惜嫉你,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,220評(píng)論 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望躏惋。 院中可真熱鬧幽污,春花似錦、人聲如沸簿姨。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,838評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)扁位。三九已至准潭,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間域仇,已是汗流浹背刑然。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,971評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留暇务,地道東北人泼掠。 一個(gè)月前我還...
    沈念sama閱讀 48,025評(píng)論 2 370
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像垦细,于是被迫代替她去往敵國(guó)和親择镇。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,843評(píng)論 2 354

推薦閱讀更多精彩內(nèi)容