在JQuery 1.5之前,Ajax仅支持一个回调函数,但在JQuery的1.5版本中,引入了 Deferred对象,它和ES6的Promise对象长的有点像,jQuery的Deferred对象也有resolve、reject、then方法,还有done、fail、always......方法。jQuery就是用这个Deferred对象来注册异步操作的回调函数,修改并传递异步操作的状态。它允许注册多个回调函数,并且能传递任何同步或异步函数的执行状态:成功或失败。简单说, Deferred对象就是jQuery的回调函数解决方案,它解决了如何处理耗时操作的问题,对那些操作提供了更好的控制,以及统一的编程接口。
一、Promise 介绍
二、Promise构造函数 和 Deferred构造函数对比
三、Deferred 介绍
jQuery.Deferred() | 创建一个新的Deferred对象的构造函数,可以带一个可选的函数参数,它会在构造完成后被调用。 |
jQuery.when() | 通过该方式来执行基于一个或多个表示异步任务的对象上的回调函数 |
jQuery.ajax() | 执行异步Ajax请求,返回实现了promise接口的jqXHR对象 |
deferred.then(resolveCallback,rejectCallback) | 添加处理程序被调用时,递延对象得到解决或者拒绝的回调。 |
deferred.done() | 当延迟成功时调用一个函数或者数组函数. |
deferred.fail() | 当延迟失败时调用一个函数或者数组函数.。 |
deferred.resolve(ARG1,ARG2,...) | 调用Deferred对象注册的‘done’回调函数并传递参数 |
deferred.resolveWith(context,args) | 调用Deferred对象注册的‘done’回调函数并传递参数和设置回调上下文 |
deferred.isResolved | 确定一个Deferred对象是否已经解决。 |
deferred.reject(arg1,arg2,...) | 调用Deferred对象注册的‘fail’回调函数并传递参数 |
deferred.rejectWith(context,args) | 调用Deferred对象注册的‘fail’回调函数并传递参数和设置回调上下文 |
deferred.promise() | 返回promise对象,这是一个伪造的deferred对象:它基于deferred并且不能改变状态所以可以被安全的传递 |
四、案例
【1】分别通过Promise和Deferred去注册异步回调函数,通过setTimeout模拟异步
对比发现,由于jquery的Deferred对象本身就有resolve方法,所以我们在创建deferred对象的时候并未像promise那样传入了一个函数作为参数,在后面可以直接deferred.resolve()这样调用
// Deferred
function asyncFunc1() {
var deferred = $.Deferred();
//做一些异步操作
setTimeout(function () {
deferred.resolve('成功');
}, 2000);
return deferred;
}
asyncFunc1().then(function (data) {
console.log(data) // 成功
});
// Promise
function asyncFunc2() {
var promise = new Promise(function (resolve, reject) {
//做一些异步操作
setTimeout(function () {
resolve('成功');
}, 2000);
});
return promise;
}
asyncFunc2().then(function (data) {
console.log(data) // 成功
});
【2】then的链式调用
// Deferred
function asyncFunc1() {
var defered = $.Deferred();
//做一些异步操作
setTimeout(function () {
defered.resolve('成功');
}, 2000);
return defered;
}
asyncFunc1().then(function (data) {
console.log(data)
return asyncFunc2()
}).then(function (data) {
console.log(data);
return asyncFunc3();
}).then(function (data) {
console.log(data);
});
【3】then方法的参数,done与fail语法糖
在Promise中then(onfulfilled,onrejected)方法中有两个参数,两个参数都是函数,第一个参数执行的是resolve()方法(即异步成功后的回调方法),第二参数执行的是reject()方法(即异步失败后的回调方法)(第二个参数可选)。在Deferred中进行了增强,还可以接受第三个参数,就是在pending状态时的回调 。除此之外,jquery还增加了两个语法糖方法,done和fail,分别用来指定执行完成和执行失败的回调
// Deferred
function asyncFunc1() {
var defered = $.Deferred();
//做一些异步操作
setTimeout(function () {
if ( /*异步操作成功*/ ) {
defered.resolve(success)
} else {
defered.reject(error)
}
}, 2000);
return defered;
}
asyncFunc1.then(function (success) {
// 异步操作成功在这里执行
// 对应于上面的resolve(success)方法
}, function (error) {
// 异步操作失败在这里执行
// 对应于上面的reject(error)方法
});
// 还可以写成这样 (推荐使用这种写法)
asyncFunc1.done(function (success) {
// 异步操作成功在这里执行
// 对应于上面的resolve(success)方法
}).fail(function (error) {
// 异步操作失败在这里执行
// 对应于上面的reject(error)方法
});
【4】always的用法
Deferred对象上还有一个always方法,不论执行完成还是执行失败,always都会执行,有点类似promise中的finally方法
asyncFunc1.done(function (success) {
// 异步操作成功在这里执行
// 对应于上面的resolve(success)方法
}).fail(function (error) {
// 异步操作失败在这里执行
// 对应于上面的reject(error)方法
}).always(function() {
// 都会执行
})
五、$.when方法
jquery中,还有一个when方法来实现Promise,与Promise中的all方法功能类似,Promise的all方法提供了并行执行异步操作的能力,并且在所有异步操作都执行完毕后才执行回调,只要其中一个异步操作返回的状态为rejected那么Promise.all()返回的Promise即为rejected状态。
两者的区别是,接受的参数不同,Promise.all方法接受一个数组作为参数,但每个参数必须是一个Promise实例,而$.when方法接受多个Deferred对象,用逗号隔开
// $.when()
$.when(asyncFunc1(), asyncFunc2(), asyncFunc3())
.then(function (data1, data2, data3) {
console.log('全部执行完成');
console.log(data1, data2, data3);
}).fail(function (error) {
})
// Promise.all()
Promise.all([asyncFunc1(), asyncFunc2(), asyncFunc3()])
.then((success) => {
console.log(success)
}).catch((error) => {
console.log(error)
})
当需要传入不确定数量的Deferred对象参数到$.when时,我们可以使用apply方法传入数组作为参数去解决这个问题
function asyncFunc1() {
var defered = $.Deferred();
setTimeout(function () {
defered.resolve('成功')
}, 200)
return defered
}
// 生成5个异步回调函数,添加到数组
let arr = []
for (let i = 0; i < 5; i++) {
arr.push(asyncFunc1())
}
// 传入数组作为参数
$.when.apply(this, arr)
.then(function (...args) {
// 获取执行结果数据
// es6
console.log(args)
// es5
var res=[].slice.apply(arguments)
console.log(res)
})
文章每周持续更新,可以微信搜索「 前端大集锦 」第一时间阅读,回复【视频】【书籍】领取200G视频资料和30本PDF书籍资料