在之前的ajax(promise)/libs/request.js)说过JQuery中也有封装好的Promise方法,所以详细介绍一下JQuery的Promise。
jQuery的Promise其实也就是Deferred对象,先来看其中有些什么方法:
1 | var def = $.Deferred() |
可以看到Deferred对象包含了许多的方法,比如done、fail、then等方法。jquery就是通过这个对象来操作回调函数,修改并且传递异步操作的状态。
为了与ajax区分开,先以setTimerout为例:1
2
3
4
5
6
7
8
9
10
11function runAsync(){
var def = $.Deferred();
setTimeout(function(){
console.log("执行完成");
def.resolve("已完成")
},1000)
return def;
}
runAsync().then(function(data){
console.log(data);
})
而ES6中的Promise是这样的:1
2
3
4
5
6
7
8
9
10
11
12
13function runAsync(){
var p = new Promise(function(resolve,rejected){
setTimeout(function(){
console.log("执行完成");
resolve("已完成")
},1000)
})
return p;
}
runAsync()
.then(function(data){
console.log(data);
})
区别在于JQuery中是封装好的resolve方法,所以不需要在创建def对象时传入参数函数,而这并不影响使用def.resolve函数。而这样写带来的一个缺点就是,如果在外部修改def对象的resolve方法,那么内部的方法就会失效。1
2
3
4
5
6
7
8
9
10
11
12
13function runAsync(){
var def = $.Deferred();
setTimeout(function(){
console.log("执行完成");
def.resolve("已完成")
},1000)
return def;
}
var d = runAsync();
d.then(function(data){
console.log(data);
})
d.resolve("外部结束");
显然不会等待1秒之后输出”已完成”,而是直接输出了”外部结束”,在异步操作完成之前,还没有resolve就在外部执行resolve。解决方法是jQuery的def对象上提供promise方法,它可以返回一个受限的Deferred对象,即没有resolve、reject方法,无法从外部改变他们的状态:1
2
3
4
5
6
7
8function runAsync(){
var def = $.Deferred();
setTimeout(function(){
console.log("执行完成");
def.resolve("已完成")
},1000)
return def.promise();//在此处调用
}
此promise非彼Promise,它是Deferred对象下的方法,作用只是为了返回一个受限的Deferred对象。这样做之后可以避免在外部修改的resolve影响内部的异步回调真身。
then的链式调用
Deferred也遵循Promise规范,所以特性基本一致,所以也可以使用then方法。
done与fail
Promise规范中then( )接收两个参数,分别是resolve和rejected的回调函数,而JQuery进行了增强,还可以接受第三个参数,就是等待态(pending)下的回调:1
deferred.then( doneFilter [, failFilter ] [, progressFilter ] )
除此之外还增加了两个语法糖的方法,done和fail,分别制定执行完成和执行失败的回调1
2
3
4
5
6d.then(function(){
console.log('执行完成');
})
.catch(function(){
console.log('执行失败')
});
等价的用法:1
2
3
4
5
6d.done(function(){
console.log('执行完成');
})
.fail(function(){
console.log('执行失败');
});
always
Deferred对象上还有一个always方法,执行完成或是失败都会触发,类似于ajax的complete。
$.when方法
该方法作用等同于于Promise.all(),在所有的异步操作执行完毕后再执行回调函数。但是该方法是单独的方法,并没有定义在$.Deferred中,与all方法的参数不同的是,接收的并不是参数,而是多个Deferred对象1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35function runAsync1(){
var p = new Promise(function(resolve, reject){
//做一些异步操作
setTimeout(function(){
console.log('异步任务1执行完成');
resolve('随便什么数据1');
}, 1000);
});
return p;
}
function runAsync2(){
var p = new Promise(function(resolve, reject){
//做一些异步操作
setTimeout(function(){
console.log('异步任务2执行完成');
resolve('随便什么数据2');
}, 2000);
});
return p;
}
function runAsync3(){
var p = new Promise(function(resolve, reject){
//做一些异步操作
setTimeout(function(){
console.log('异步任务3执行完成');
resolve('随便什么数据3');
}, 2000);
});
return p;
}
$.when(runAsync1(), runAsync2(), runAsync3())
.then(function(data1, data2, data3){
console.log('全部执行完成');
console.log(data1, data2, data3);
});
但是jquery并没有ES6中的Promise的race方法。
ajax与Deferred
以前的ajax方法中有success、error、complete方法,分别代表请求成功、失败、结束的回调。而自从JQuery引入Promise之后,Deferred和以上三个方法就建立起了联系,success—done,error—fail,complete—always,也就是语法糖而已。看一下源码:1
2
3deferred.promise( jqXHR ).complete = completeDeferred.add;
jqXHR.success = jqXHR.done;
jqXHR.error = jqXHR.fail;