浅谈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这部分的代码,可是没能读懂,但愿有大神可以给一些实现思路

相关文章
相关标签/搜索