淺談Javascript中Promise對象的實現

本文同步自我得博客:http://www.joeray61.com前端

不少作前端的朋友應該都據說過Promise(或者Deferred)對象,今天我就講一下我對Promise的認識web

What?

PromiseCommonJS的規範之一,擁有resolverejectdonefailthen等方法,可以幫助咱們控制代碼的流程,避免函數的多層嵌套。現在異步在web開發中愈來愈重要,對於開發人員來講,這種非線性執行的編程會讓開發者以爲難以掌控,而Promise可讓咱們更好地掌控代碼的執行流程,jQuery等流行的js庫都已經實現了這個對象,年末即將發佈的ES6也將原生實現Promiseajax

Why

想象這樣一個場景,兩個異步請求,第二個須要用到第一個請求成功的數據,那麼咱們代碼能夠這樣寫編程

ajax({
        url: url1,
        success: function(data) {
            ajax({
                url: url2,
                data: data,
                success: function() {
                }
            });
        }
    });

若是繼續下去在回調函數中進行下一步操做,嵌套的層數會愈來愈多。咱們能夠進行適當的改進,把回調函數寫到外面數組

function A() {
        ajax({
            url: url1,
            success: function(data) {
                B(data);
            }
        });
    }
    function B(data) {
        ajax({
            url: url2,
            success: function(data) {
                ......
            }
        });
    }

即便是改寫成這樣,代碼仍是不夠直觀,可是若是有了Promise對象,代碼就能夠寫得很是清晰,一目瞭然,請看異步

new Promise(A).done(B);

這樣函數B就不用寫在A的回調中了函數

How

目前的ES標準中還未支持Promise對象,那麼咱們就本身動手,豐衣足食吧。思路大體是這樣的,用2個數組(doneListfailList)分別存儲成功時的回調函數隊列和失敗時的回調隊列this

  • state: 當前執行狀態,有pendingresolvedrejected3種取值url

  • done: 向doneList中添加一個成功回調函數prototype

  • fail: 向failList中添加一個失敗回調函數

  • then: 分別向doneListfailList中添加回調函數

  • always: 添加一個不管成功仍是失敗都會調用的回調函數

  • resolve: 將狀態更改成resolved,並觸發綁定的全部成功的回調函數

  • reject: 將狀態更改成rejected,並觸發綁定的全部失敗的回調函數

  • when: 參數是多個異步或者延遲函數,返回值是一個Promise兌現,當全部函數都執行成功的時候執行該對象的resolve方法,反之執行該對象的reject方法
    下面是個人具體實現過程:

var Promise = function() {
    this.doneList = [];
    this.failList = [];
    this.state = 'pending';
};

Promise.prototype = {
    constructor: 'Promise',
    resolve: function() {
        this.state = 'resolved';
        var list = this.doneList;
        for(var i = 0, len = list.length; i < len; i++) {
            list[0].call(this);
            list.shift();
        }
    },
    reject: function() {
        this.state = 'rejected';
        var list = this.failList;
        for(var i = 0, len = list.length; i < len; i++){
            list[0].call(this);
            list.shift();
        }
    },
    done: function(func) {
        if(typeof func === 'function') {
            this.doneList.push(func);
        }
        return this;
    },
    fail: function(func) {
        if(typeof func === 'function') {
            this.failList.push(func);
        }
        return this;
    },
    then: function(doneFn, failFn) {
        this.done(doneFn).fail(failFn);
        return this;
    },
    always: function(fn) {
        this.done(fn).fail(fn);
        return this;
    }
};

function when() {
    var p = new Promise();
    var success = true;
    var len = arguments.length;
    for(var i = 0; i < len; i++) {
        if(!(arguments[i] instanceof Promise)) {
            return false;
        }
        else {
            arguments[i].always(function() {
                if(this.state != 'resolved'){
                    success = false;
                }
                len--;
                if(len == 0) {
                    success ? p.resolve() : p.reject();
                }
            });
        }
    }
    return p;
}

Improve

目前只是實現了Promise的基礎功能,但仍然還有沒法處理的狀況,例如要實現3個或3個以上的異步請求的串行,目前個人Promise沒有辦法支持new Promise(A).then(B).then(C)這樣的形式,jQuery在1.7的版本中爲Deferred(Promise)對象實現了pipe函數,能夠經過這個函數實現上述功能,代碼爲$.Deferred(A).pipe(B).then(C),我嘗試去讀了jQuery這部分的代碼,可是沒能讀懂,但願有大神可以給一些實現思路

相關文章
相關標籤/搜索