Promise實現

Promise對象是一個代理對象(代理一個值)雁刷,被代理的值在Promise對象創(chuàng)建時可能是未知的。它允許你為異步操作的成功和失敗分別綁定相應的處理方法(handlers)保礼。這讓異步方法可以像同步方法那樣返回值沛励,但并不是立即返回最終執(zhí)行結果,而是一個能代表未來出現的結果的promise對象炮障。

一個Promise有以下幾種狀態(tài):

  • pending: 初始狀態(tài)目派,成功或失敗狀態(tài)。
  • fulfilled: 意味著操作成功完成胁赢。
  • rejected: 意味著操作失敗企蹭。

Promise的一般使用方式如下:

var p = new Promise(function(resolve, reject) {
    setTimeout(function() {
        resolve('Resolved!');
    }, 500);
});
p.then(function(msg) {
    console.log('Promise has been ' + msg);
}).catch(function(err) {
    console.log('Promise has an error!');
});

注:如果你不是很了解Promise及其使用,可以查看一下官方文檔Promise

根據這種鏈式調用方式智末,我們可以推測一下Promise內部大概是什么結構:

  • 有一個變量练对,用于記錄當前Promise對象的狀態(tài)
  • 有一個變量,用于存儲當前Promise對象的數據
  • 有一個數組吹害,用于存儲成功時的回調
  • 有一個數組,用于存儲失敗時的回調
  • 有一個then方法虚青,用于傳入resolve和reject方法來消費Promise
  • 有一個catch方法它呀,用于捕獲異常

所以目前為止,Promise的內部構造大致為:

function Promise(executor) {
    var self = this;
    self.status = 'pending';
    self.data = undefined;
    self.onReslovedCallback = [];
    self.onRejectedCallback = [];
}
Promise.prototype.then = function(onResolved, onRejected) { // todo }
Promise.prototype.catch = function(onRejected) { // todo }

回到之前的調用舉例中棒厘,我們看到傳入的函數中有兩個參數纵穿,一個為resolve,另一個reject奢人,以我們的使用經驗可以了解到谓媒,resolve會將結果推送至下一個then中,而reject會將結果推送至下一個catch中何乎。這兩個皆為函數句惯,所以我們可以添加一些東西到Promise中:

function Promise(executor) {
    var self = this;
    self.status = 'pending';
    self.data = undefined;
    self.onReslovedCallback = [];
    self.onRejectedCallback = [];

    function resolve(data) { // todo  }
    function reject(reason) { // todo }

    try {
        executor(resolve, reject);
    }
    catch(err) {
        reject(err);
    }
}

resolve的作用是將Promise對象狀態(tài)置為resolved土辩,同時將數據傳遞給各個回調,然后再執(zhí)行回調方法抢野,所以我們能夠寫出這個方法:

function Promise(executor) {
    // ...

    function resolve(data) {
        if(self.status === 'pending') {
            self.status = 'resolved';
            self.data = value;
            for(var i=0;i<self.onReslovedCallback.length;i++) {
                self.onReslovedCallback[i](value);
            }
        }
    }
    
    // ...
}

同理拷淘,我們能夠寫出reject方法:

function Promise(executor) {
    // ...

    function reject(reason) {
        if(self.status === 'pending') {
            self.status = 'rejected';
            self.data = reason;
            for(var i=0;i<self.onRejectedCallback.length;i++) {
                self.onRejectedCallback[i](reason);
            }
        }
    }
    
    // ...
}

對于then方法,我們從調用可以看出指孤,它將消費Promise的方式傳入進來启涯。當然,Promise里存在有三種狀態(tài)恃轩,我們需要分別處理:

Promise.prototype.then = function(onResolved, onRejected) {
    var self = this;
    var promise2;

    // 當出現promise.then().then()...這種調用時结洼,我們直接將數據進行返回,這樣可以實現穿透
    onResolved = typeof onResolved === 'function' ? onResolved : function(v) {
        return v;
    };

    // 跟上面一樣叉跛,為了實現穿透松忍,直接將數據進行返回
    onRejected = typeof onRejected === 'function' ? onRejected : function(r) {
        return r;
    };

    // 當Promise對象狀態(tài)為resolved時,直接返回一個新的Promise對象昧互,這個對象中挽铁,直接調用onResolved方法
    if(self.status === 'resolved') {
        return promise2 = new Promise(function(resolve, reject) {
            try {
                var x = onResolved(self.data);
                if(x instanceof Promise) {
                    x.then(resolve, reject);
                }
                resolve(x);
            }
            catch(err) {
                reject(err);
            }
        });
    }

    // 當Promise對象狀態(tài)為rejected時,直接返回一個新的Promise對象敞掘,這個對象中直接調用onRejected方法
    if(self.status === 'rejected') {
        return promise2 = new Promise(function(resolve, reject) {
            try {
                var x = onRejected(self.data);
                if(x instanceof Promise) {
                    x.then(resolve, reject);
                }
            }
            catch(err) {
                reject(err);
            }
        });
    }

    // 當Promise對象為pending時叽掘,直接返回一個新的Promise對象,這個對象中需要將onResolved以及onRejected方法存入相應的回調列表中
    if(self.status === 'pending') {
        return promise2 = new Promise(function(resolve, reject) {
            self.onReslovedCallback.push(function(value) {
                try {
                    var x = onResolved(self.data);
                    if(x instanceof Promise) {
                        x.then(resolve, reject);
                    }
                }
                catch(err) {
                    reject(err);
                }
            });

            self.onRejectedCallback.push(function(reason) {
                try {
                    var x = onRejected(self.data);
                    if(x instanceof Promise) {
                        x.then(resolve, reject);
                    }
                }
                catch(err) {
                    reject(err);
                }
            });
        });
    }
}

最后實現一下catch方法玖雁,很簡單更扁,直接調用then進行處理就可以了:

Promise.prototype.catch = function(onRejected) {
    return this.then(null, onRejected);
};

至此,我們實現了一個簡單Promise對象赫冬,可以嘗試去使用一下(我們在node中進行測試浓镜,所以需要將其導出module.exports = Promise):

var Promise = require('./promise');

var p = new Promise(function(resolve, reject) {
    setTimeout(function() {
        console.log(1);
        resolve();
    }, 500);
});

p.then(function(resolve, reject) {
    console.log(2);
    resolve();
});

// 執(zhí)行結果
// 1
// 2

References

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末膛薛,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子补鼻,更是在濱河造成了極大的恐慌哄啄,老刑警劉巖,帶你破解...
    沈念sama閱讀 207,113評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件风范,死亡現場離奇詭異咨跌,居然都是意外死亡,警方通過查閱死者的電腦和手機硼婿,發(fā)現死者居然都...
    沈念sama閱讀 88,644評論 2 381
  • 文/潘曉璐 我一進店門锌半,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人寇漫,你說我怎么就攤上這事刊殉⊙乘ぃ” “怎么了?”我有些...
    開封第一講書人閱讀 153,340評論 0 344
  • 文/不壞的土叔 我叫張陵冗澈,是天一觀的道長钦勘。 經常有香客問我,道長亚亲,這世上最難降的妖魔是什么彻采? 我笑而不...
    開封第一講書人閱讀 55,449評論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮捌归,結果婚禮上肛响,老公的妹妹穿的比我還像新娘。我一直安慰自己惜索,他們只是感情好特笋,可當我...
    茶點故事閱讀 64,445評論 5 374
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著巾兆,像睡著了一般猎物。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上角塑,一...
    開封第一講書人閱讀 49,166評論 1 284
  • 那天蔫磨,我揣著相機與錄音,去河邊找鬼圃伶。 笑死堤如,一個胖子當著我的面吹牛,可吹牛的內容都是我干的窒朋。 我是一名探鬼主播搀罢,決...
    沈念sama閱讀 38,442評論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼侥猩!你這毒婦竟也來了榔至?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 37,105評論 0 261
  • 序言:老撾萬榮一對情侶失蹤欺劳,失蹤者是張志新(化名)和其女友劉穎洛退,沒想到半個月后,有當地人在樹林里發(fā)現了一具尸體杰标,經...
    沈念sama閱讀 43,601評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 36,066評論 2 325
  • 正文 我和宋清朗相戀三年彩匕,在試婚紗的時候發(fā)現自己被綠了腔剂。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,161評論 1 334
  • 序言:一個原本活蹦亂跳的男人離奇死亡驼仪,死狀恐怖掸犬,靈堂內的尸體忽然破棺而出袜漩,到底是詐尸還是另有隱情,我是刑警寧澤湾碎,帶...
    沈念sama閱讀 33,792評論 4 323
  • 正文 年R本政府宣布宙攻,位于F島的核電站,受9級特大地震影響介褥,放射性物質發(fā)生泄漏座掘。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,351評論 3 307
  • 文/蒙蒙 一柔滔、第九天 我趴在偏房一處隱蔽的房頂上張望溢陪。 院中可真熱鬧,春花似錦睛廊、人聲如沸形真。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,352評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽咆霜。三九已至,卻和暖如春嘶朱,著一層夾襖步出監(jiān)牢的瞬間蛾坯,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,584評論 1 261
  • 我被黑心中介騙來泰國打工见咒, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留偿衰,地道東北人。 一個月前我還...
    沈念sama閱讀 45,618評論 2 355
  • 正文 我出身青樓改览,卻偏偏與公主長得像下翎,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子宝当,可洞房花燭夜當晚...
    茶點故事閱讀 42,916評論 2 344

推薦閱讀更多精彩內容

  • 本文適用的讀者 本文寫給有一定Promise使用經驗的人视事,如果你還沒有使用過Promise,這篇文章可能不適合你庆揩,...
    HZ充電大喵閱讀 7,296評論 6 19
  • Promise 對象 Promise 的含義 Promise 是異步編程的一種解決方案俐东,比傳統(tǒng)的解決方案——回調函...
    neromous閱讀 8,698評論 1 56
  • Promiese 簡單說就是一個容器,里面保存著某個未來才會結束的事件(通常是一個異步操作)的結果订晌,語法上說虏辫,Pr...
    雨飛飛雨閱讀 3,348評論 0 19
  • 00砌庄、前言Promise 是異步編程的一種解決方案,比傳統(tǒng)的解決方案——回調函數和事件——更合理和更強大。它由社區(qū)...
    夜幕小草閱讀 2,128評論 0 12
  • 從前娄昆,你說佩微,時光很慢 你喜歡一個人坐在階前 摘花,聽雨 像個安靜的少年 那些從遠方飄來的信箋 滿載著深深思念 卻在...
    邊城幻影閱讀 274評論 27 5