最清晰简便的promise js插件实现方式,以及promise的用法
2017-03-31 23:40

promise介绍

promise已经出现了很长时间了,但是由于一般是前端库提供,所以单独使用的比较少。目前ES6的规范有promise,并且FireFox,Chrome新版本等浏览器已经支持了promise原生对象,但是为了兼容性,我们最好还是使用自己实现的promise对象。

promise的定义介绍网上有不少,但都是些术语的介绍,让人看着云里雾里,玄之又玄的,实在晦涩难懂。我们还是用大白话来说说promise,其实promise就是一个对象,用来把ajax等异步调用通过链式的写法实现回调函数,其实目的就是为了美化书写,让代码运行跟书写顺序是一致的,提高代码的可读性。

promise的另类实现

promise网上也有不少实现方式,但是本文我采用的实现方式跟网上的实现方式不同,结构更加清晰,更容易懂。

我们先假定我们实现的promise的目标调用如下:

//创建promise对象
var prom=PP.promise();
//链式调用
prom.start(function(){
    ....
    this.resolve(msg);
}).then(function(value){
    ...
    this.resolve(msg);
}).then(function(value){
    ...
    this.resolve(msg);
});

//顺序调用
prom.then(function(value){
    ...
    this.resolve(msg);
});
prom.then(function(value){
    ...
});

这个promise对象,通过start开始启动,之后通过then方法等待异步执行完成后再执行,多个then可以链式调用,也可以分开顺序书写,通过resolve方法来通知异步调用完成,然后调用下一个then方法。

我也不一步步贴代码了,直接把所有的代码贴出来,然后再讲解。

(function(_){
    var promise=function(){
        return new promise.fn.init();
    }
    promise.fn=promise.prototype={
        //初始化promise,回调函数数组。
        init:function(){
            this.state="pending";
            this.thenarr=[];
        },
        //异步完成时调用,
        resolve:function(msg){
            this.state="done";
            this.execThen("success,final",msg);
        },
        //异步错误时调用,
        reject:function(msg){
            this.state="error";
            this.execThen("error,final",msg);
        },
        //无论正确,错误都调用,
        final:function(msg){
            this.state="final";
            this.execThen("final",msg);
        },
        //执行then添加的回调函数,
        execThen:function(fnStr,msg){
            var fnArr=fnStr.split(",");
            this.state = "pending";
            if(this.thenarr.length>0) {
                var thenFn = this.thenarr.shift();
                var i = 0, len = fnArr.length;
                for (; i < len; i++) {
                    if (thenFn[fnArr[i]]) {
                        thenFn[fnArr[i]].call(this, msg);
                    }
                }
            }

        },
        //then方法添加回调,
        then:function(successFn,errorFn,finalFn){
            var obj={
                success:successFn,
                error:errorFn,
                final:finalFn
            };
            this.thenarr.push(obj);
            return this;
        },
        //promise开始调用的函数,
        start:function(callback){
            var th=this;
            setTimeout(function(){
                console.log(th.thenarr);
                callback.call(th);
            },0);
            return this;
        },
    };
    promise.fn.init.prototype=promise.fn;
    _.PP={
        promise:promise
    };
}(window));

由于是本于最简化实现,所以事先的promise并没有对参数就行容错处理,如果你想更完善,可以自行添加判断参数是否合法的代码。

从上面代码可以很清楚的看到我们的promise逻辑很清楚:

@1:通过var prom=PP.promise()创建promise对象。

@2:调用init方法初始化状态为“pending”,并且初始化用来存储then回调的数组。

@3:通过prom.start(function(){})开始一个异步调用的操作。

@4:通过调用then方法把thenarr数组中push对象,返回this用来链式调用。

@5:调用多个then方法,函数都push到tenarr对象中。

@6:在start函数中通过this.resolve调用表示异步执行完成,并传递msg信息。

@7:resolve方法中改变promise状态,并且调用execThen函数。

@8:execThen函数从数组中shift出来一个then的函数对象,根据传递来的参数字符串调用相应的方法,并且改变"state"状态为"pending"。

@9:在执行的then回调中通过再调用resolve函数从@7步骤重复进行。

我们实现的promise对象只有一个对象传递,然后then的回调都放到数组中了,再resolve的时候从数组中去除第一个对象进行执行,从而保证then回调的执行顺序,start中使用setTimeout是为了让开始异步启用,为了start中是同步代码时,给then函数添加回调到数组中的时间。在回调函数调用时,我们通过call方法改变this对象为promise,为了在回调中能够调用resolve等内置函数。

promise调用示例如下:

var prom=PP.promise();
prom.start(function(){
    writeMsg("start函数开始");
    var th=this;
    setTimeout(function(){
        writeMsg("start异步结束");
        th.resolve("then调用开始");
    },3000);
}).then(function(msg){
    writeMsg(msg);
},function(){},function(msg){
    writeMsg("final函数执行");
    var th=this;
    setTimeout(function(){
        th.resolve("第二个then函数开始");
    },5000);

}).then(function(msg){
    writeMsg(msg);
    var th=this;
    setTimeout(function(){
        th.resolve("顺序写法then开始");
    },4000);
});
prom.then(function(msg){
    writeMsg(msg);
    this.resolve("最后一个then执行:妹纸");
}).then(function(msg){
    writeMsg(msg+"前端");
});
var dom=document.getElementById("promdiv");
function writeMsg(msg){
    var div=document.createElement("div");
    div.innerHTML=new Date().getSeconds()+"s:-------------"+msg;
    dom.appendChild(div);
}

其中第一个then传入三个回调函数,用来start中对应resolve,reject,final方法调用的回调函数。

上面的代码调用执行后打印的顺序如下:

promise.png

调用的例子点击查看:promise插件使用例子

如果你需要此插件,可以点击这里下载此promise插件:PP_promise插件下载

调用例子中有链式调用,也有顺序书写形式的调用,有用setTimout模拟的异步,也有非异步的函数,但是要想启动下一个then的函数,必须调用resolve,reject,final中的一个方法来触发,并且在传入的回调里面最好只有一个resovle他们三个方法的调用,不然剩下的then会执行两次。非异步的调用resolve后会在执行完本身方法后直接调用resolve再调用execFn形成递归来执行。

这个插件不算完善,关于一些参数验证,resolve统一函数多次调用等等的问题没有处理,所以这个插件要想使用的话,就得正确调用才能正确运行。

promise结语

虽然自己实现一个独特实现思路的promise插件,但是也不一定完全正确,插件么,就是为使用而生的,只要能用的方便,其他的管它呢,也没有按照ES6的promise形式来实现此插件,这个是一个小遗憾,以后有时间了补一个ES6的promise的替代插件。

原创文章,转载请注明来自:妹纸前端-www.webfront-js.com.
阅读(4316)
辛苦了,打赏喝个咖啡
微信
支付宝
妹纸前端
妹纸前端工作室 | 文章不断更新中
京ICP备16005385号-1