手寫Promise(class 版本)

參考資料

本文參考資料 MDN

代碼

const PENDING = 'pending';
const RESOLVED = 'resolved';
const REJECTED = 'rejected';

class MyPromise {
    /**
   * Promise 構(gòu)造函數(shù)
   * @param {*} excutor 執(zhí)行器方法(同步執(zhí)行)
   */
    constructor(excutor) {
        this.status = PENDING; //給 promise 對象指定 status 屬性,初始值為 pending
        this.data = undefined; // 給 promise 對象指定一個用于存儲結(jié)果的屬性 
        this.callbacks = []; // 每個元素的結(jié)構(gòu)為 {onResolved(){}, onRejected(){}}

        const resolve = value => {
            // 如果當前狀態(tài)不是 pending该编,直接結(jié)束
            if (this.status !== PENDING) {
                return;
            }
            // 將狀態(tài)改為 resolved
            this.status = RESOLVED;

            // 保存 value 數(shù)據(jù)
            this.data = value;

            // 如果有待執(zhí)行的 callback 函數(shù),異步執(zhí)行回調(diào) onResolved (用setTimeout執(zhí)行異步)
            if (this.callbacks.length > 0) {
                this.callbacks.forEach(callbacksObj => { //放到隊列中執(zhí)行所有成功的回調(diào)
                    setTimeout(() => {
                        callbacksObj.onResolved(value);
                    });
                });
            }

        };

        const reject = reason => {
            // 如果當前狀態(tài)不是 pending,直接結(jié)束
            if (this.status !== PENDING) {
                return;
            }

            // 將狀態(tài)改為 rejected
            this.status = REJECTED;

            //保存 reason 數(shù)據(jù)
            this.data = reason;

            //如果有待執(zhí)行的 callback 函數(shù),異步執(zhí)行回調(diào)函數(shù) onRejected(用setTimeout執(zhí)行異步)
            if (this.callbacks.length > 0) {
                this.callbacks.forEach(callbacksObj => {
                    setTimeout(() => {
                        callbacksObj.onRejected(reason);
                    });
                });
            }
        };

        //立即同步執(zhí)行 excutor,如果執(zhí)行器函數(shù)拋出異常愕提,promise 對象變?yōu)?rejected 狀態(tài) (直接調(diào)用reject方法即可)
        try {
            excutor(resolve, reject);
        } catch (error) {
            reject(error);
        }
    }

    /**
     * Promise 原型對象的 then()
     * @param {*} onResolved 指定成功的回調(diào)函數(shù)
     * @param {*} onRejected 指定失敗的回調(diào)函數(shù) 
     * @return 返回一個新的 Promise 對象,返回的 Promise 的結(jié)由 onResolved/onRejected 的執(zhí)行結(jié)果決定
     */
    then(onResolved, onRejected) {
        onResolved = typeof onResolved === 'function' ? onResolved : value => value;

        // 指定默認的失敗回調(diào)(實現(xiàn)錯誤/異常傳透)
        onRejected = typeof onRejected === 'function' ? onRejected : reason => { throw reason; };

        return new MyPromise((resolve, reject) => {

            /**
             * 調(diào)用指定的回調(diào)函數(shù)處理,根據(jù)執(zhí)行結(jié)果皿哨,改變 return 的 promise 狀態(tài)
             * 
             * 1. 如果回調(diào)函數(shù)拋出異常浅侨,return 的 promise 就會失敗,reason 就是 error
             * 2. 如果回調(diào)函數(shù)返回的不是 promise证膨,return 的 promise 就會成功如输,value 就是返回的值
             * 3. 如果回調(diào)函數(shù)返回的是一個 promise,return 的 promise 的結(jié)果就是這個 promise 的結(jié)果
             * 
             * @param {*} callback 
             */
            const hander = (callback) => {
                try {
                    const result = callback(this.data);
                    if (result instanceof MyPromise) { // 3. 
                        result.then(resolve, reject); // 2.
                    } else {
                        resolve(result);
                    }
                } catch (error) { //1.
                    reject(error);
                }
            };

            switch (this.status) {
                case PENDING: // 如果當前是 pending 狀態(tài),將回調(diào)函數(shù)保存起來不见,等 status 改變后在執(zhí)行
                    this.callbacks.push(
                        {
                            onResolved: value => hander(onResolved),
                            onRejected: reason => hander(onRejected)
                        });
                    break;
                case RESOLVED: // 如果當前是 resolved 狀態(tài)澳化,異步執(zhí)行 onResolved,并改變 return 的 promise 狀態(tài)
                    setTimeout(() => {
                        hander(onResolved);
                    });
                    break;
                case REJECTED: // 如果當前是 rejected 狀態(tài)稳吮,異步執(zhí)行 onRejected肆捕,并改變 return 的 promise 狀態(tài)
                    setTimeout(() => {
                        hander(onRejected);
                    });
                    break;
                default:
                    break;
            }
        });
    };

    /**
     * Promise 原型對象的 catch()
     * @param {*} onRejected 指定失敗的回調(diào)函數(shù)
     * @return  返回一個新的 Promise 對象
     */
    catch(onRejected) {
        return this.then(undefined, onRejected);
    };

    /**
     * Promsie 函數(shù)對象方法(靜態(tài)方法)
     * @param {*} value 
     * @return 返回一個指定結(jié)果的成功的 Promise 對象
     */
    static resolve(value) {
        return new MyPromise((resolve, reject) => {
            if (value instanceof MyPromise) {
                value.then(resolve, reject);
            } else {
                resolve(value);
            }
        });
    };

    /**
     * Promise 函數(shù)對象方法(靜態(tài)方法)
     * @param {*} reason  失敗的原因
     * @return 返回一個指定 reason 的失敗的 Promise 對象
     */
    static reject(reason) {
        return new MyPromise((resolve, reject) => {
            reject(reason);
        });
    };


    /**
     * Promise 函數(shù)對象的 all 方法(靜態(tài)方法)
     * @param {*} myPromises 
     * @return 返回一個 promise 只有當所有的 promise 都成功時才成功,否則只要有一個失敗則失敗
     */
    static all(myPromises) {
        //用來保存所有成功 value 的數(shù)組
        const values = Array(myPromises.length);

        //用來保存成功 promise 的數(shù)量
        let resolvedCount = 0;
        return new MyPromise((resolve, reject) => {
            // 遍歷獲取每個Promise的結(jié)果
            myPromises.forEach((p, index) => {
                //p成功盖高,將成功的 value 保存到 values 中 
                //MyPromise.resolve(p) 說明:myPromises中的的元素有可能不是一個Promise對象慎陵,
                //用 MyPromise.resolve 將其包裝成一個promise對象
                MyPromise.resolve(p).then(value => {
                    resolvedCount++;
                    values[index] = value;

                    //如果全部成功了,將 return 的 promise 改為成功
                    if (resolvedCount === myPromises.length) {
                        resolve(values);
                    }
                }, reason => { //只要有一個失敗了喻奥,return 的 promise 就失敗
                    reject(reason);
                });
            });
        });

    };

    /**
     * Promise 函數(shù)對象 race 方法 (類里面靜態(tài)方法)
     * @param {*} myPromises 
     * @return 返回一個 promise席纽,其結(jié)果由第一個完成的 promise 的結(jié)果決定
     */
    static race(myPromises) {
        return new MyPromise((resolve, reject) => {
            //返回第一個執(zhí)行完成的 promise 的結(jié)果
            myPromises.forEach((p) => {
                //MyPromise.resolve(p) 說明:myPromises中的的元素有可能不是一個Promise對象,
                //用 MyPromise.resolve 將其包裝成一個promise對象
                MyPromise.resolve(p).then(value => {
                    resolve(value);
                }, reason => {
                    reject(reason);
                });
            });
        });
    };
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末撞蚕,一起剝皮案震驚了整個濱河市润梯,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌甥厦,老刑警劉巖纺铭,帶你破解...
    沈念sama閱讀 219,427評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異刀疙,居然都是意外死亡舶赔,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,551評論 3 395
  • 文/潘曉璐 我一進店門谦秧,熙熙樓的掌柜王于貴愁眉苦臉地迎上來竟纳,“玉大人,你說我怎么就攤上這事疚鲤∽独郏” “怎么了?”我有些...
    開封第一講書人閱讀 165,747評論 0 356
  • 文/不壞的土叔 我叫張陵集歇,是天一觀的道長桶略。 經(jīng)常有香客問我,道長诲宇,這世上最難降的妖魔是什么际歼? 我笑而不...
    開封第一講書人閱讀 58,939評論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮焕窝,結(jié)果婚禮上蹬挺,老公的妹妹穿的比我還像新娘。我一直安慰自己它掂,他們只是感情好巴帮,可當我...
    茶點故事閱讀 67,955評論 6 392
  • 文/花漫 我一把揭開白布溯泣。 她就那樣靜靜地躺著,像睡著了一般榕茧。 火紅的嫁衣襯著肌膚如雪垃沦。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,737評論 1 305
  • 那天用押,我揣著相機與錄音肢簿,去河邊找鬼。 笑死蜻拨,一個胖子當著我的面吹牛池充,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播缎讼,決...
    沈念sama閱讀 40,448評論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼收夸,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了血崭?” 一聲冷哼從身側(cè)響起卧惜,我...
    開封第一講書人閱讀 39,352評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎夹纫,沒想到半個月后咽瓷,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,834評論 1 317
  • 正文 獨居荒郊野嶺守林人離奇死亡舰讹,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,992評論 3 338
  • 正文 我和宋清朗相戀三年茅姜,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片跺涤。...
    茶點故事閱讀 40,133評論 1 351
  • 序言:一個原本活蹦亂跳的男人離奇死亡匈睁,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出桶错,到底是詐尸還是另有隱情,我是刑警寧澤胀蛮,帶...
    沈念sama閱讀 35,815評論 5 346
  • 正文 年R本政府宣布院刁,位于F島的核電站,受9級特大地震影響粪狼,放射性物質(zhì)發(fā)生泄漏退腥。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,477評論 3 331
  • 文/蒙蒙 一再榄、第九天 我趴在偏房一處隱蔽的房頂上張望狡刘。 院中可真熱鬧,春花似錦困鸥、人聲如沸嗅蔬。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,022評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽澜术。三九已至艺蝴,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間鸟废,已是汗流浹背猜敢。 一陣腳步聲響...
    開封第一講書人閱讀 33,147評論 1 272
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留盒延,地道東北人缩擂。 一個月前我還...
    沈念sama閱讀 48,398評論 3 373
  • 正文 我出身青樓,卻偏偏與公主長得像添寺,于是被迫代替她去往敵國和親撇叁。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 45,077評論 2 355