前言
Deferred是从1.5版本引入的一个核心特性之一,主要是为了解决Callback Hell,老生常谈的问题,这里就不多赘述了。本文旨在剖析Deferred的内部实现,让大家能够深入了解Deferred。
API
$.Deferred
通过调用$.Deferred()获取到一个Deferred实例,如下
var dd = $.Deferred();
调用的时候可以传入一个函数(可以简单的看作构造函数),如下
var dd = $.Deferred(function (newDefer) {
newDefer.name = 'My Deferred';
});
console.log(dd.name);//My Deferred
resolve/done reject/fail notify/progress
Deferred的实现使用了闭包,内部维护了三个$.Callbacks管理队列。
可以说Deferred的核心在于$.Callbacks。如果不熟悉$.Callbacks的请自行搜索或参考我的另一篇介绍文章jQuery Callbacks。代码可以简化成下面的样子
var resolveCb = jQuery.Callbacks("once memory");
var rejectCb = jQuery.Callbacks("once memory");
var notifyCb = jQuery.Callbacks("memory");
done = resolveCb.add
resolve ~ resolveCb.fire
resolveWith = resolveCb.fireWith
fail = rejectCb.add
reject ~ rejectCb.fire
rejectWith = rejectCb.fireWith
progress = notifyCb.add
notify ~ notifyCb.fire
notifyWith = notifyCb.fireWith
~表示可以简单理解为对等。我们再看看下面的代码会不会有不一样的感觉了
var dd = $.Deferred();
dd.done(function (name) {
console.log(name, 1);
}).done(function (name) {
console.log(name, 2);
});
dd.resolve('Jacky');
其实就相当于
resolveCb.add(function (name) {
console.log(name, 1);
}).add(function (name) {
console.log(name, 2);
});
resolveCb.fire('Jacky');
当Deferred执行完resolve以后,同时会调用rejectCb.disable和notifyCb.lock。
当Deferred执行完reject以后,同时会调用resolveCb.disable和notifyCb.lock。
notifyCb就是一个单纯的$.Callbacks,但是它的状态会受到resolve/reject的影响。
resolveCb和rejectCb两者之间是相互限制的,一旦两者中的某一个fire了,另一个就会被disable。通过这种方式来达到唯一状态。
resolve/resolveWith reject/rejectWith notify/notifyWith
就是fire和firewith的区别
state
返回Deferred的当前状态,总共有三种状态
执行resolve/reject前,返回值是pending
执行了resolve,返回值是resolved
-
执行了reject,返回值是rejected
var dd = $.Deferred();
console.log(dd.state());//pending
dd.resolve();
console.log(dd.state());//resolved
//dd.reject();
//console.log(dd.state());//rejected
always
看下源码大家就一清二楚了
还是调用的done和fail,往各自队列里添加回调。
promise
提供Deferred的非状态接口。比较下Deferred和Deferred.promise()
var dd = $.Deferred();
var promise = dd.promise();
console.log(dd);
console.log(promise);
也就是说promise返回值是Deferred的一个非状态操作的子集,允许我们添加回调,但是不允许我们操作Deferred的状态。也可以将这些方法添加到某一个对象上,例如
var obj = {name: 'xxx'};
var dd = $.Deferred();
var newObj = dd.promise(obj);
console.log(newObj);
console.log(newObj === obj);
then/pipe
pipe是为了向下兼容留下的方法,pipe===then。then支持三个参数,返回值是一个新的promise对象。看几个例子
例子一
var dd = $.Deferred();
dd.done(function (name) {
console.log('dd', name);
});
dd.then(function (name) {
console.log('then', name);
});
dd.resolve('Jacky');
//dd Jacky
//then Jacky
例子二
var dd = $.Deferred();
dd.fail(function (name) {
console.log('dd', name);
});
dd.then(null, function (name) {
console.log('then', name);
});
dd.reject('Jacky');
//dd Jacky
//then Jacky
例子三
var dd = $.Deferred();
dd.progress(function (name) {
console.log('dd', name);
});
dd.then(null, null, function (name) {
console.log('then', name);
});
dd.notify('Jacky');
//dd Jacky
//then Jacky
三个参数刚好分别添加到了内部的三个回调队列中。dd.then(fn)可以简单看作dd.done(fn)。前面说了then的返回值是一个新的promise对象,如果在新的Deferred对象上继续添加回调会怎么样呢?我们分两种情况来看。
then方法的返回值不是Deferred对象
var dd = $.Deferred();
var newdd = dd.then(function (name) {
console.log('then', name);
return 'Mary';
});
newdd.done(function (name) {
console.log('newDefer', name);
});
dd.resolve('Jacky');
//then Jacky
//newDefer Mary
then的返回值会传递给done/fail的参数。而且无需我们手动调用newdd.resolve,内部帮我们调用了
返回值是Deferred对象
看个例子
var dd = $.Deferred();
var returndd = $.Deferred();
var newdd = dd.then(function (name) {
console.log('then', name);
return returndd;
});
newdd.done(function (name) {
console.log('newDefer', name);
});
dd.resolve('Jacky');//then Jacky
newdd.done添加的函数没有马上执行了。我们手动调用下returndd.resolve
var dd = $.Deferred();
var returndd = $.Deferred();
var newdd = dd.then(function (name) {
console.log('then', name);
return returndd;
});
newdd.done(function (name) {
console.log('newDefer', name);
});
dd.resolve('Jacky');//then Jacky
returndd.resolve('Helen');//newDefer Helen
这里就是我们的promise编程了
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。