Javascript Promise 学习(上)

Promise 就是处理异步的一个规范方法

a();
b();
alert("a");
如果a() 里面有一个ajax 或者settimeout 
那么alert("a") 会先跑
这就是异步了。
从前我们用一堆callBack函数来解决问题,但是这样写不好看。
promise 的写法美丽多了
依据上面的例子
a().then(b).then(function(){
alert("");
})
这样它会先跑完 a -> b - > alert("");

虽然很多类库都实现了这个功能,甚至游览器也有自带的,而且ecma6好像还会有更好更简单的方法来解决异步的操作 ! 
but ! 人生最鸡巴的就是这个 but! 
当作学习,我还是自己写了一个版本。
这个版本不是 promise a 也不是promise a+ 也不是jQuery Defferd 规范
它是给我自己用的!

个人觉得重点思想就是在 then 的时候返回一个 next promise , 而next promise 记入在上一个promise中,这样就创建了一个promise 连环链

而一个回调函数返回的是promise 或者我们叫thenAble函数,那么我们就把原先的链连接去它的后面就可以了。 

     function Promise(fn) {
        //如果有放fn进来,那么就调用它,把 pass & fail 做参数传进去
        //这样fn 内就可以直接 publish了 
        if (G.isFunction(fn)) {
            var promise = new Promise();
            fn.apply(null, [promise.pass.bind(promise), promise.fail.bind(promise)]);
            return promise;
        }
        this.status = "pending";
        this.data;
        this._nextPromise; //保存下一个的指针,这样才可以连环触发
        this._passFn_list = [];
        this._failFn_list = [];
        this._isAllPassRequest = true; //all or any 用的
        //注释
        //要改要潜水
        //这里只说简单的使用说明吧 
        /*
        //方便的初始化调用法
        new Promise(function (pass, fail) {
            setTimeout(pass, 100, "zzz"); //第三para是传给方法的参数 = pass(zzz)
        }).then(fnPass, fnFail);
        //一般调用法
        a().then(b).then(c);
        function a() {
            var promise = new Promise();
            setTimeout(function () {
                promise.pass("data");
            }, 1000);
            return promise;
        }
        //all 和 any 参数是array,它会并发处理 
        //等待全部处理完验证每个的返回
        //unknow 算 true 
        //(note: 一个promise只保存all or any 1condition default是 all)
        //如果要求是 all ,那么回来的全部都必须all 就调用nextPromise.pass else fail
        //如果要求是 any ,那么回来的要有至少一个是pass 就调用nextPromise.pass else fail
        a().all([b, c], [c, b]).then(b, c);
        //.catch, .complete 都是语法糖罢了
    
        //异步loop调用, 这个比较复杂,以后还可以有更多变化
        var list = [a, a, a];
        var promiseTemp;
        for (var i = 0; i < list.length; i++) {
            var promise = (promiseTemp == undefined) ? list[i]() : promiseTemp.then(list[i]);
            promiseTemp = promise;
        }
        */
        //简单说明:
        /*
        两大关键方法
        then : 把参数加入fn_list中, 然后new 新的promise, 当前的promise.next 指向新的promise(循环链就开始了) ,然后把新的promise 返回出去
        pass/fail : 把fn_list中的方法全部拿出来,然后"并发处理", 检查每一个是不是返回promise, 是的话要记入和跟踪,跟踪其实就是为这个promise加一个then,然后把职责链放进去,交替的感觉。
     
        调用then/all/any的时候如果promise的status不是pending会直接add and 发布data
        在并发方法之后 fnList会被清除干净,也就是说一个promise的fnlist的每个方法只会被执行一次
        */
    }
    Promise.prototype._concatFnList = function (passFn_list, failFn_list) {
        //all 和 any 用来concat fn list 的
        if (passFn_list != undefined && Array.isArray(passFn_list)) {
            this._passFn_list = this._passFn_list.concat(passFn_list);
        }
        if (failFn_list != undefined && Array.isArray(failFn_list)) {
            this._failFn_list = this._failFn_list.concat(failFn_list);
        }
    }
    Promise.prototype._getFinalDataByDataRecordList = function (dataRecord_list) {
        //dataRecord_list 是array , 因为大部分时候我们是用一个data罢了,所以这边做一些小过滤
        var finalValue;
        if (dataRecord_list.length == 1) {
            finalValue = dataRecord_list[0].data; //抽取data出来就够了
        }
        else {        
            dataRecord_list.orderBy("index");//排好位置(因为all/any是并发,不会按照顺序回来)
            finalValue = dataRecord_list.map(function (obj) {
                delete obj.index; //洗掉属性 index 排位之后就没用了
                return obj;
            });
        }
        return finalValue;
    }
    Promise.prototype._publish = function (fn_list, that) {
        //pass 和 fail 的共用过程
        if (fn_list.length > 0) {
            var returnPromiseCountRecord = 0; //for all & any 多个并发,同时会有多个promise函数
            var dataRecord_list = []; //收集全部函数回来的data {index,data,status}                              
            for (var i = 0, l = fn_list.length; i < l; i++) {
                var fn = fn_list[i];
                var returnValue = fn.call(that, that.data); //挺关键的一句,这里会确定他返回的是不是promise
                (function (i) {
                    function callBack(data) {
                        //这里是我们外面的promise链 publish触发的 
                        var result = { index: i, status: this.status, data: data };
                        dataRecord_list.push(result) //record data
                        returnPromiseCountRecord--; //之前我们累加计算,这里publish了就要开始累减回去了
                        //一直到0就准备做ending然后继续外面的职责链了
                        if (returnPromiseCountRecord == 0) {
                            var is_allPass = dataRecord_list.every(function (v) {
                                return (v.status == "unknow" || v.status == "pass");
                            });
                            var is_anyPass = dataRecord_list.some(function (v) {
                                return (v.status == "unknow" || v.status == "pass");
                            });
                            var finalValue = that._getFinalDataByDataRecordList(dataRecord_list);
                            //这里会依据 all or any 做处理 觉得调用下一家的 pass or fail
                            if ((that._isAllPassRequest && is_allPass) || (!that._isAllPassRequest && is_anyPass)) {
                                that._nextPromise.pass(finalValue);
                            }
                            else {
                                that._nextPromise.fail(finalValue);
                            }
                        }
                    }
                    if (returnValue instanceof Promise) {
                        //是promise的话就要 +count,然后+then给他,把职责链给他,等待他触发
                        returnPromiseCountRecord++;
                        var otherPromise = returnValue;
                        otherPromise.complete(callBack);
                    }
                    else {
                        //不是的话就push data 就好,status : unknow 也算 pass
                        var result = { index: i, status: "unknow", data: returnValue };
                        dataRecord_list.push(result);
                    }
                })(i)
            }
            fn_list.length = 0; //运行完就clear掉
            //没有return任何thenable方法
            if (returnPromiseCountRecord == 0) {
                var finalValue = that._getFinalDataByDataRecordList(dataRecord_list);
                that._nextPromise.pass(finalValue); //因为没有pass fail 参考所以一定是pass 
            }
        }
        else {
            //如果没有效应就去找小一家promise效应
            if (that._nextPromise) {
                that._nextPromise[that.status](that.data);
            }
        }
    }
    Promise.prototype.all = function (passFn_list, failFn_list) {
        this._concatFnList(passFn_list, failFn_list);
        if (this.status != "pending") this._alreadyPublish();
        return this._nextPromise || (this._nextPromise = new Promise());
    }
    Promise.prototype.any = function (passFn_list, failFn_list) {
        this._concatFnList(passFn_list, failFn_list);
        this._isAllPassRequest = false;
        if (this.status != "pending") this._alreadyPublish();
        return this._nextPromise || (this._nextPromise = new Promise());
    }
    Promise.prototype.then = function (passFn, failFn) {
        if (passFn != undefined) this._passFn_list.push(passFn);
        if (failFn != undefined) this._failFn_list.push(failFn);
        if (this.status != "pending") this._alreadyPublish();
        return this._nextPromise || (this._nextPromise = new Promise());
    }
    Promise.prototype._alreadyPublish = function () {
        var that = this;
        setTimeout(function () {
            that[that.status](that.data);
        }, 0);
    }
    Promise.prototype.pass = function (data) {
        var that = this;
        this.status = "pass";
        this.data = data;
        var passFn_list = this._passFn_list;
        that._publish(passFn_list, that);
    }
    Promise.prototype.fail = function (data) {
        var that = this;
        this.status = "fail";
        this.data = data;
        var failFn_list = this._failFn_list;
        that._publish(failFn_list, that);
    }
    Promise.prototype.complete = function (completeFn) {
        return this.then(completeFn, completeFn);
    }
    Promise.prototype.catch = function (catchFn) {
        return this.then(void 0, catchFn);
    }
    Promise.prototype.getLastPromise = function () {
        var promise = this;
        for (var i = 0; i < Number.MAX_VALUE; i++) {
            if (promise._nextPromise === undefined) {
                return promise;
            }
            else {
                promise = promise._nextPromise;
            }
        }
    }
    G.s.Promise = Promise;

 

posted @ 2014-08-07 10:47  兴杰  阅读(463)  评论(0编辑  收藏  举报