promise的原理

Promise 類似于一個事務(wù)管理器,它的作用就是將各種內(nèi)嵌回調(diào)的事務(wù)用流水形式表達(dá)畦浓。利用 Promise 可以讓異步編程更符合人的直覺,讓代碼邏輯更加清晰,把開發(fā)人員從回調(diào)地獄中釋放出來诗越。

基礎(chǔ)概念

目前, Promise 是 ECMAScript 6 規(guī)范的重要特性之一息堂,各大瀏覽器也開始慢慢支持這一特性嚷狞。當(dāng)然,也有一些第三方內(nèi)庫實(shí)現(xiàn)了該功能荣堰,如: Q 床未、 when 、 WinJS 振坚、 RSVP.js 等薇搁。

Promise 對象用來進(jìn)行延遲( deferred )和異步( asynchronous )計(jì)算。一個 Promise 處于以下四種狀態(tài)之一:

  • pending: 還沒有得到肯定或者失敗結(jié)果渡八,進(jìn)行中
  • fulfilled: 成功的操作
  • rejected: 失敗的操作
  • settled: 已被 fulfilled 或 rejected

Promise 對象有兩個重要的方法啃洋,一個是 then 传货,另一個是 resolve :

  • then:將事務(wù)添加到事務(wù)隊(duì)列中
  • resolve:開啟流程,讓整個操作從第一個事務(wù)開始執(zhí)行

Promise 常用方式如下:

var p = new Promise(function(resolve, reject) {
 ... // 事務(wù)觸發(fā) resovle(xxx); ... 
 });
p.then(function(value) { // 滿足 };
function(reason) { // 拒絕 }).then().then()...

實(shí)現(xiàn)步驟

Promise 其實(shí)就是一個狀態(tài)機(jī)宏娄。按照它的定義问裕,我們可從如下基礎(chǔ)代碼開始:

var PENDING = 0;   // 進(jìn)行中
var FULFILLED = 1; // 成功 
var REJECTED = 2;  // 失敗 
function Promise() { 
    var state = PENDING;// 存儲PENDING, FULFILLED或者REJECTED的狀態(tài) 
    var value = null;// 存儲成功或失敗的結(jié)果值 
    var handlers = []; // 存儲成功或失敗的處理程序,通過調(diào)用`.then`或者`.done`方法 

    function fulfill(result) { // 成功狀態(tài)變化 
        state = FULFILLED;
        value = result; 
    }

    function reject(error) { // 失敗狀態(tài)變化 
        value = error; 
    }
}

2.下面是 Promise 的 resolve 方法實(shí)現(xiàn):

注意: resolve 方法可接收的參數(shù)有兩種:

  • 一個普通的值/對象
  • 一個 Promise 對象孵坚。
    如果是普通的值/對象粮宛,則直接把結(jié)果傳遞到下一個對象;
    如果是一個 Promise 對象十饥,則必須先等待這個子任務(wù)序列完成窟勃。
function Promise(){ 
    ... 
    function resolve(result){
        try {
            var then = getThen(result); 
            // 如果是一個promise對象 
            if (then) {  
                 doResolve(then.bind(result), resolve, reject); 
                 return;
             }
             // 修改狀態(tài),傳遞結(jié)果到下一個事務(wù) 
             fulfill(result); 
         } catch (e) { 
             reject(e); 
         }
    }
}

兩個輔助方法:

/** * Check if a value is a Promise and, if it is, 
* return the `then` method of that promise. 
* * @param {Promise|Any} value  
* * @return {Function|Null} 
* */  
function getThen(value) { 
    var t = typeof value;  
    if (value && (t === 'object' || t === 'function')) { 
        var then = value.then;  
        if (typeof then === 'function') { 
            return then;  
        } 
    }
     return null;
}
/** * Take a potentially misbehaving resolver function and make sure   
* onFulfilled and onRejected are only called once. 
* Makes no guarantees about asynchrony. 
* @param {Function} fn A resolver function that may not be trusted 
* @param {Function} onFulfilled 
* @param {Function} onRejected  
* /
function doResolve(fn, onFulfilled, onRejected) {
    var done = false; 
    try { 
        fn(function(value) {
            if (done) return; 
            done = true;  
            onFulfilled(value); },
         function(reason) { 
          if (done) return; 
          done = true;  
          onRejected(reason); });  
    } catch(ex) {
         if (done) return;
         done = true; 
         onRejected(ex); 
      }
} 
3.上面已經(jīng)完成了一個完整的內(nèi)部狀態(tài)機(jī)逗堵,但我們并沒有暴露一個方法去解析或則觀察 Promise ”酰現(xiàn)在讓我們開始解析 Promise :
function Promise(fn) { 
... 
doResolve(fn, resolve, reject); 
}

如你所見,我們復(fù)用了 doResolve 蜒秤,因?yàn)閷τ诔跏蓟?fn 也要對其進(jìn)行控制汁咏。 fn 允許調(diào)用 resolve 或則 reject 多次,甚至拋出異常作媚。這完全取決于我們?nèi)ケWC promise 對象僅被 resolved 或則 rejected 一次攘滩,且狀態(tài)不能隨意改變。

4.目前纸泡,我們已經(jīng)有了一個完整的狀態(tài)機(jī)漂问,但我們?nèi)匀粵]有辦法去觀察它的任何變化。我們最終的目標(biāo)是實(shí)現(xiàn) then 方法女揭,但 done 方法似乎更簡單蚤假,所以讓我們先實(shí)現(xiàn)它。

我們的目標(biāo)是實(shí)現(xiàn) promise.done(onFullfilled, onRejected) :

  • onFulfilled 和 onRejected 兩者只能有一個被執(zhí)行吧兔,且執(zhí)行次數(shù)為一
  • 該方法僅能被調(diào)用一次, 一旦調(diào)用了該方法磷仰,則 promise 鏈?zhǔn)秸{(diào)用結(jié)束
  • 無論是否 promise 已經(jīng)被解析,都可以調(diào)用該方法
var PENDING = 0; // 進(jìn)行中 
var FULFILLED = 1; // 成功 
var REJECTED = 2; // 失敗  
function Promise() { 
    var state = PENDING;  // 存儲PENDING, FULFILLED或者REJECTED的狀態(tài) 
    var value = null;  // 存儲成功或失敗的結(jié)果值  
    var handlers = []; // 存儲成功或失敗的處理程序境蔼,通過調(diào)用`.then`或者`.done`方法  
    // 成功狀態(tài)變化  
    function fulfill(result) {  
        state = FULFILLED;  
        value = result; 
        handlers.forEach(handle);  
        handlers = null;  
     } 
    // 失敗狀態(tài)變化  
    function reject(error) {  
        state = REJECTED; 
        value = error;  
        handlers.forEach(handle); 
        handlers = null;  
    } 
    function resolve(result) {   
        try { 
            var then = getThen(result);  
            if (then) {   
                doResolve(then.bind(result), resolve, reject) 
                return  
             }  
             fulfill(result); 
        } catch (e) {
             reject(e); 
        } 
    }
    // 不同狀態(tài)灶平,進(jìn)行不同的處理  
    function handle(handler) { 
        if (state === PENDING) {  
            handlers.push(handler); 
        } else {  
            if (state === FULFILLED && typeof handler.onFulfilled === 'function') {  
                handler.onFulfilled(value); 
            }  
             if (state === REJECTED && typeof handler.onRejected === 'function') { 
                 handler.onRejected(value);  
             } 
        }  
    } 

    this.done = function (onFulfilled, onRejected) {  
        // 保證異步 
        setTimeout( 
            function () { 
                handle({ 
                    onFulfilled: onFulfilled,  
                    onRejected: onRejected });  
            }, 0); 
    }  
    doResolve(fn, resolve, reject); 
 }

當(dāng) Promise 被 resolved 或者 rejected 時,我們保證 handlers 將被通知箍土。

5.現(xiàn)在我們已經(jīng)實(shí)現(xiàn)了 done 方法逢享,下面實(shí)現(xiàn) then 方法就很容易了。需要注意的是吴藻,我們要在處理程序中新建一個 Promise 拼苍。
this.then = function (onFulfilled, onRejected) { 
var self = this; 
return new Promise( 
function (resolve, reject) {  
    return self.done(
    function (result) { 
        if (typeof onFulfilled === ‘function’) {  
            try {  
                // onFulfilled方法要有返回值!  
                return resolve(onFulfilled(result));  
            } catch (ex) {  
                return reject(ex);  
            } 
        } else {  
            return resolve(result);
        }  
    },  
    function (error) {  
        if (typeof onRejected === ‘function’) {  
            try { 
                return resolve(onRejected(error));
            } catch (ex) {  
                return reject(ex);  
            }  
        } else {  
            return reject(error);  
        } 
    });  
    });  
}
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市疮鲫,隨后出現(xiàn)的幾起案子吆你,更是在濱河造成了極大的恐慌,老刑警劉巖俊犯,帶你破解...
    沈念sama閱讀 217,542評論 6 504
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件妇多,死亡現(xiàn)場離奇詭異,居然都是意外死亡燕侠,警方通過查閱死者的電腦和手機(jī)者祖,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,822評論 3 394
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來绢彤,“玉大人七问,你說我怎么就攤上這事∶2埃” “怎么了械巡?”我有些...
    開封第一講書人閱讀 163,912評論 0 354
  • 文/不壞的土叔 我叫張陵,是天一觀的道長饶氏。 經(jīng)常有香客問我讥耗,道長,這世上最難降的妖魔是什么疹启? 我笑而不...
    開封第一講書人閱讀 58,449評論 1 293
  • 正文 為了忘掉前任古程,我火速辦了婚禮,結(jié)果婚禮上喊崖,老公的妹妹穿的比我還像新娘挣磨。我一直安慰自己,他們只是感情好荤懂,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,500評論 6 392
  • 文/花漫 我一把揭開白布趋急。 她就那樣靜靜地躺著,像睡著了一般势誊。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上谣蠢,一...
    開封第一講書人閱讀 51,370評論 1 302
  • 那天粟耻,我揣著相機(jī)與錄音,去河邊找鬼眉踱。 笑死挤忙,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的谈喳。 我是一名探鬼主播册烈,決...
    沈念sama閱讀 40,193評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了赏僧?” 一聲冷哼從身側(cè)響起大猛,我...
    開封第一講書人閱讀 39,074評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎淀零,沒想到半個月后挽绩,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,505評論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡驾中,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,722評論 3 335
  • 正文 我和宋清朗相戀三年唉堪,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片肩民。...
    茶點(diǎn)故事閱讀 39,841評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡唠亚,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出持痰,到底是詐尸還是另有隱情灶搜,我是刑警寧澤,帶...
    沈念sama閱讀 35,569評論 5 345
  • 正文 年R本政府宣布共啃,位于F島的核電站占调,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏移剪。R本人自食惡果不足惜究珊,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,168評論 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望纵苛。 院中可真熱鬧剿涮,春花似錦、人聲如沸攻人。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,783評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽怀吻。三九已至瞬浓,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間蓬坡,已是汗流浹背猿棉。 一陣腳步聲響...
    開封第一講書人閱讀 32,918評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留屑咳,地道東北人萨赁。 一個月前我還...
    沈念sama閱讀 47,962評論 2 370
  • 正文 我出身青樓,卻偏偏與公主長得像兆龙,于是被迫代替她去往敵國和親杖爽。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,781評論 2 354

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