promise原理淺析

Promise超神超簡單總結:
之前總感覺promise很神祕,今天仔細讀了阮一峯大神的es6,也查詢了下相關的promise原理,終於明白了promise怎麼這麼6.

簡介:

Promise 對象用於延遲(deferred) 計算和異步(asynchronous )計算.一個Promise對象代表着一個還未完成,但預期將來會完成的操作。Promise 對象是一個返回值的代理,這個返回值在promise對象創建時未必已知。它允許你爲異步操作的成功或失敗指定處理方法。 這使得異步方法可以像同步方法那樣返回值:異步方法會返回一個包含了原返回值的 promise 對象來替代原返回值。

解決了什麼問題及怎麼使用:

//一個簡單的示例 執行一個動畫A,執行完之後再去執行另一個動畫B
    setTimeout(function(){
        //A動畫
        console.log('A');
        setTimeout(function() {
            //B動畫
            console.log('B');
        },300)
    },300);
//這裏只有兩個動畫,如果有更多呢,就會看到一堆函數縮進

不難想象,如果依次有很多個動畫,就會出現多重嵌套。代碼不是縱向發展,而是橫向發展,很快就會亂成一團,無法管理。因爲多個異步操作形成了強耦合,只要有一個操作需要修改,它的上層回調函數和下層回調函數,可能都要跟着修改。這種情況就稱爲回調函數地獄“(callback hell)

Promise 對象就是爲了解決這個問題而提出的。它不是新的語法功能,而是一種新的寫法,允許將回調函數的嵌套,改成鏈式調用。
Promise 的最大問題是代碼冗餘,原來的任務被 Promise 包裝了一下,不管什麼操作,一眼看去都是一堆then,原來的語義變得很不清楚。

(1)瀏覽器實現方式 可以在支持Promise的版本上運行

var p = new Promise(function(resolve, reject){
  setTimeout(function(){
    //A動畫
    console.log('A');
    resolve();
  },300);
});

p.then(function(){
  setTimeout(function() {
      //B動畫
      console.log('B');
  },300);
});

(2)另一種寫法(jQuery版本)

var deferred = $.Deferred();
setTimeout(function(){
  //A動畫
  console.log('A');
  deferred.resolve();
},300);

deferred.done(function() {
  setTimeout(function() {
    //B動畫
    console.log('B');
  },300)
});

promise會讓代碼變得更容易維護,像寫同步代碼一樣寫異步代碼。

promise原理

其實,promise就是三個狀態。利用觀察者模式,只需要通過特定書寫方式註冊對應狀態的事件處理函數,然後更新狀態,調用註冊過的處理函數即可。
這個特定方式就是then,done,fail,always…等方法,更新狀態就是resolve、reject方法。

下面簡單實現:

/**
    /**
      * [3種狀態]
      * @type {String}
     */
    var PENDING = "pending";
    var RESOLVED = "resolved";
    var REJECTED = "rejected";
    /**
     * [Promise類實現]
     * 構造函數傳入一個fn,有兩個參數,resolve:成功回調; reject:失敗回調;
     * state: 狀態存儲
     * doneList: 成功處理函數列表
     * failList: 失敗處理函數列表
     * done: 註冊成功處理函數
     * fail: 註冊失敗處理函數
     * then: 同時註冊成功和失敗處理函數
     * always: 一個處理註冊到成功和失敗,都會調用
     * resolve: 更新state爲:RESOLVED,並且執行成功處理隊列
     * reject: 更新state爲:REJECTED,並且執行失敗處理隊列
    */
    var Promise2 = (function() {
        var noop = function() {}
        function Promise(fn) {
            this.state = PENDING;
            this.doneList = [];
            this.failList = [];
            this.fn = fn;
            this.fn(this.resolve.bind(this), this.reject.bind(this)) //立即執行構造函數的第一個參數
        }
        var p = {
            done: function(cb) {

                if (typeof cb == "function")
                    this.doneList.push(cb)
                return this;
            },
            fail: function(cb) {
                if (typeof cb == "function")
                    this.failList.push(cb);
                return this;
            },
            then: function(success, fail) {
                this.done(success || noop).fail(fail || noop)
                return this;
            },
            always: function(cb) {
                this.done(cb).fail(cb)
                return this;
            },
            resolve: function() {
                this.state = RESOLVED;
                var lists = this.doneList;
                for (var i = 0; i < lists.length;) {
                    lists[i].apply(this, arguments);
                    lists.shift();
                }
                return this;
            },
            reject: function() {
                this.state = REJECTED;
                var lists = this.failList;
                for (var i = 0; i < lists.length;) {
                    lists[0].apply(this, arguments);
                    lists.shift();
                }
                return this;
            }
        }
        for (var k in p) {
            Promise.prototype[k] = p[k]
        }
        return Promise;
    })();
//測試代碼
var p1 =  new Promise2(function(resolve,reject){    
    setTimeout(resolve,2000);
    console.log('p1',new Date);
 });
 p1.then(function(){
    console.log("end",new Date);
 }).always(function(){
    console.log('always',new Date);
 });

利用觀察者模式,只需要通過特定書寫方式,註冊對應狀態的事件處理函數,然後更新狀態,調用註冊過的處理函數即可。

解釋:當我們調用new Promise(fn)時,就會立即執行第一個參數fn。上面案例中,先將then(done,fail)對應的回調函數分別加入到doneList或者failList中,always中對應的參數同時加到doneList和failList中,當異步代碼執行完畢,就調用resolve方法,此時改變promise的狀態爲resolved,並執行doneList裏面的回調函數。如果執行失敗,則調用fail方法,此時改變promise的狀態爲rejected,並執行failList裏面的回調函數。

4、generator的一個小應用

function * gen(m,n){
    for(let i=m;i<n;i++){
        yield i;
    }
}
console.log(...gen(1,10));

//結果: 1 2 3 4 5 6 7 8 9

參考鏈接:
Promise: http://imweb.io/topic/565af932bb6a753a136242b0

發佈了171 篇原創文章 · 獲贊 44 · 訪問量 29萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章