大白话讲解Promise(三)搞懂jquery中的Promise

http://www.cnblogs.com/lvdabao/p/jquery-deferred.html @吕大豹html

前两篇咱们讲了ES6中的Promise以及Promise/A+规范,在Promise的知识体系中,jquery固然是必不可少的一环,因此本篇就来说讲jquery中的Promise,也就是咱们所知道的Deferred对象。
 
事实上,在此以前网上有不少文章在讲jquery Deferred对象了,可是总喜欢把ajax和Deferred混在一块儿讲,容易把人搞混。when、done、promise、success、error、fail、then、resolve、reject、always这么多方法不能揉在一块儿讲,须要把他们捋一捋,哪些是Deferred对象的方法,哪些是ajax的语法糖,咱们须要心知肚明。
 

先讲$.Deferred

jquery用$.Deferred实现了Promise规范,$.Deferred是个什么玩意呢?仍是老方法,打印出来看看,先有个直观印象:
var def = $.Deferred();
console.log(def);
输出以下:
$.Deferred()返回一个对象,咱们能够称之为Deferred对象,上面挂着一些熟悉的方法如:done、fail、then等。jquery就是用这个Deferred对象来注册异步操做的回调函数,修改并传递异步操做的状态。
 
Deferred对象的基本用法以下,为了避免与ajax混淆,咱们依旧举setTimeout的例子:
复制代码
function runAsync(){
    var def = $.Deferred();
    //作一些异步操做
    setTimeout(function(){
        console.log('执行完成');
        def.resolve('随便什么数据');
    }, 2000);
    return def;
}
runAsync().then(function(data){
    console.log(data)
});
复制代码
在runAsync函数中,咱们首先定义了一个def对象,而后进行一个延时操做,在2秒后调用def.resolve(),最后把def做为函数的返回。调用runAsync的时候将返回def对象,而后咱们就能够.then来执行回调函数。
 
是否是感受和ES6的Promise很像呢?咱们来回忆一下第一篇中ES6的例子:
复制代码
function runAsync(){
    var p = new Promise(function(resolve, reject){
        //作一些异步操做
        setTimeout(function(){
            console.log('执行完成');
            resolve('随便什么数据');
        }, 2000);
    });
    return p;           
}
runAsync()
复制代码
区别在何处一看便知。因为jquery的def对象自己就有resolve方法,因此咱们在建立def对象的时候并未像ES6这样传入了一个函数参数,是空的。在后面能够直接def.resolve()这样调用。
 
这样也有一个弊端,由于执行runAsync()能够拿到def对象,而def对象上又有resolve方法,那么岂不是能够在外部就修改def的状态了?好比我把上面的代码修改以下:
var d = runAsync();
d.then(function(data){
    console.log(data)
});
d.resolve('在外部结束');
现象会如何呢?并不会在2秒后输出“执行完成”,而是直接输出“在外部结束”。由于咱们在异步操做执行完成以前,没等他本身resolve,就在外部给resolve了。这显然是有风险的,好比你定义的一个异步操做并指定好回调函数,有可能被别人给提早结束掉,你的回调函数也就不能执行了。
 
怎么办?jquery提供了一个promise方法,就在def对象上,他能够返回一个受限的Deferred对象,所谓受限就是没有resolve、reject等方法,没法从外部来改变他的状态,用法以下:
复制代码
function runAsync(){
    var def = $.Deferred();
    //作一些异步操做
    setTimeout(function(){
        console.log('执行完成');
        def.resolve('随便什么数据');
    }, 2000);
    return def.promise(); //就在这里调用
}
复制代码
这样返回的对象上就没有resolve方法了,也就没法从外部改变他的状态了。这个promise名字起的有点奇葩,容易让咱们搞混,其实他就是一个返回受限Deferred对象的方法,与Promise规范没有任何关系,仅仅是名字叫作promise罢了。虽然名字奇葩,可是推荐使用。
 

then的链式调用

既然Deferred也是Promise规范的实现者,那么其余特性也必须是支持的。链式调用的用法以下:
复制代码
var d = runAsync();

d.then(function(data){
    console.log(data);
    return runAsync2();
})
.then(function(data){
    console.log(data);
    return runAsync3();
})
.then(function(data){
    console.log(data);
});
复制代码
与咱们第一篇中的例子基本同样,能够参照。
 

done与fail

咱们知道,Promise规范中,then方法接受两个参数,分别是执行完成和执行失败的回调,而jquery中进行了加强,还能够接受第三个参数,就是在pending状态时的回调,以下:
deferred.then( doneFilter [, failFilter ] [, progressFilter ] )
除此以外,jquery还增长了两个语法糖方法,done和fail,分别用来指定执行完成和执行失败的回调,也就是说这段代码:
d.then(function(){
    console.log('执行完成');
}, function(){
    console.log('执行失败');
});
与这段代码是等价的:
复制代码
d.done(function(){
    console.log('执行完成');
})
.fail(function(){
    console.log('执行失败');
});
复制代码
 

always的用法

jquery的Deferred对象上还有一个always方法,不论执行完成仍是执行失败,always都会执行,有点相似ajax中的complete。不赘述了。
 

$.when的用法

jquery中,还有一个$.when方法来实现Promise,与ES6中的all方法功能同样,并行执行异步操做,在全部的异步操做执行完后才执行回调函数。不过$.when并无定义在$.Deferred中,看名字就知道,$.when,它是一个单独的方法。与ES6的all的参数稍有区别,它接受的并非数组,而是多个Deferred对象,以下:
$.when(runAsync(), runAsync2(), runAsync3())
.then(function(data1, data2, data3){
    console.log('所有执行完成');
    console.log(data1, data2, data3);
});
jquery中没有像ES6中的race方法吗?就是以跑的快的为准的那个方法。对的,jquery中没有。
 
以上就是jquery中Deferred对象的经常使用方法了,还有一些其余的方法用的也很少,干脆就不记它了。接下来该说说ajax了。
 

ajax与Deferred的关系

jquery的ajax返回一个受限的Deferred对象,还记得受限的Deferred对象吧,也就是没有resolve方法和reject方法,不能从外部改变状态。想一想也是,你发一个ajax请求,别人从其余地方给你取消掉了,也是受不了的。
 
既然是Deferred对象,那么咱们上面讲到的全部特性,ajax也都是能够用的。好比链式调用,连续发送多个请求:
复制代码
req1 = function(){
    return $.ajax(/*...*/);
}
req2 = function(){
    return $.ajax(/*...*/);
}
req3 = function(){
    return $.ajax(/*...*/);
}

req1().then(req2).then(req3).done(function(){
    console.log('请求发送完毕');
});
复制代码
明白了ajax返回对象的实质,那咱们用起来就驾轻就熟了。
 

success、error与complete

这三个方法或许是咱们用的最多的,使用起来是这样的:
$.ajax(/*...*/)
.success(function(){/*...*/})
.error(function(){/*...*/})
.complete(function(){/*...*/})
分别表示ajax请求成功、失败、结束的回调。这三个方法与Deferred又是什么关系呢?其实就是语法糖,success对应done,error对应fail,complete对应always,就这样,只是为了与ajax的参数名字上保持一致而已,更方便你们记忆,看一眼源码:
deferred.promise( jqXHR ).complete = completeDeferred.add;
jqXHR.success = jqXHR.done;
jqXHR.error = jqXHR.fail;
complete那一行那么写,是为了减小重复代码,其实就是把done和fail又调用一次,与always中的代码同样。deferred.promise( jqXHR )这句也能看出,ajax返回的是受限的Deferred对象。
 
jquery加了这么些个语法糖,虽然上手门槛更低了,可是却形成了必定程度的混淆。一些人虽然这么写了好久,却一直不知道其中的原理,在面试的时候只能答出一些皮毛,这是很很差的。这也是我写这篇文章的原因。
 
jquery中Deferred对象涉及到的方法不少,本文尽可能分门别类的来介绍,但愿能帮你们理清思路。总结一下就是:$.Deferred实现了Promise规范,then、done、fail、always是Deferred对象的方法。$.when是一个全局的方法,用来并行运行多个异步任务,与ES6的all是一个功能。ajax返回一个Deferred对象,success、error、complete是ajax提供的语法糖,功能与Deferred对象的done、fail、always一致。就酱。
相关文章
相关标签/搜索