zepto
的Deferred
模块是用于处理异步的一个模块,不得不说这些人真的厉害,刚开始哪有什么谷歌,百度可以用。前天看到一个沸点,就是谷歌的工程师有多厉害:就是他们在造谷歌的时候,并没有前人可以借鉴的经验。
结构
;(function ($) {
function Deferred (func) {
deferred = {};
promise = {
state: function () {},
always: function () {},
then: function () {},
promise: function () {}
}
promise.promise(deferred);
if (func) func.call(deferred, deferred);
return deferred
}
})
复制代码
可以看到内部返回的是一个deferred
对象,deferred
在promise
进行扩展,除了有promise
的方法,在此基础上扩展了另外的方法。
Deferred对象
来看具体的实现。
var tuples = [
['resolve', 'done', $.Callbacks({once: 1, memory: 1}), 'resolved'],
['reject', 'fail', $.Callbacks({once: 1, memory: 1}), 'rejected'],
['notify', 'progress', $.Callbacks({memory: 1})]
],
state = 'pending',
$.each(tuples, function (i, tuple) {
var list = tuple[2],
stateString = tuple[3]
promise[tuple[1]] = list.add;
})
复制代码
tuples包含的有操作,添加回调,回调列表,和状态。state
表示当前的状态,promise
的状态有pending
,resolved
已完成,reject
被拒绝。然后遍历tuples
, list
是$.Callbacks
工厂方法生成的管理回调函数的一系列方法,stateString
就表示当前的状态,可以看到只有前两项有状态描述。然后在promise
上添加done
,fail
,progress
方法,这些方法实质是往回调列表中添加回调函数。
if (stateString) {
list.add(function() {
state = stateString;
}, tuples[i^1][2].disable, tuples[2][2].lock)
}
deferred[tuple[0]] = function () {
deferred[tuple[0] + 'With'](this === deferred ? promise : this, arguments);
return this;
}
deferred[tuple[0] + 'With'] = list.fireWith;
复制代码
如果stateString
存在,就往回调列表中添加函数,如果i为0,就是状态变为resolve
,0^1的结果就是1,把tuples[i^1][2]
就是rejected
的回调列表禁用。这样就实现了成功和失败的状态互斥,做的状态锁定,不能更改。
下面就是添加resolve/resolveWith
,reject/rejectWith
,notify/notifyWith
方法。这些With方法都是添加到deferred对象上,promise
上并没有这些方法,deferred
对象上调用resolved
方法事实上也是调用resolvedWith
方法,也就是调用$.Callbacks()上的fireWith
方法。方法调用的时候,如果当前是deferred
对象,上下文就是promise
。
when
$.when = function (sub) {
var resolveValues = slice.call(arguments),
len = resolveValues.length,
i = 0,
remain = len !== 1 || (sub && $.isFunction(sub.promise)) ? len : 0,
deferred = remain === 1 ? sub : Deferred(),
progressValues, progressContexts, resolveContexts,
}
复制代码
上面是when
方法,在JQuery中的使用如下:$.when(异步方法).done(res = > {// 操作})。上面是一些变量的初始化:
- resolveValues: 所有的异步对象,用
slice
方法转换为真正的数组 - len: 异步对象的个数
- i: 当前异步对象的索引
- remain: 判断假如参数只有一个,那么这个参数是不是异步对象,如果是异步对象则返回1,否则返回0。
- deferred: 如果参数只有1个,并且是异步对象则返回传入的参数
sub
,否则新建一个Deferred
对象。 - progressValues: 进度回调函数数组
- progressContexts: 进度回调函数绑定的上下文数组
- resolveContexts: 成功回调函数绑定的上下文数组
updateFn = function (i, ctx, val) {
return function (value) {
ctx[i] = this;
val[i] = arguments.length > 1 ? slice.call(arguments) : value;
if (val === progressValues) {
deferred.notifyWith(ctx, val)
} else if (!(--remain)) {
deferred.resolveWith(ctx, val)
}
}
}
复制代码
这是updateFn方法,主要是在不同阶段调用不同的方法。下面是参数的含义:
- i: 表示是异步对象的索引值
- ctx: 当前的上下文对象
- val: 回调函数的参数数组 如果是
progress
的回调,则调用deferred
的notifyWith
方法。否则就是remain
减少1,如果全部回调都完成了,就执行deferred
的resolveWith
方法。
if (len > 1) {
progressValues = new Array(len);
progressContexts = new Array(len);
resolveContexts = new Array(len);
for (; i < len; ++i) {
if (resolveValues[i] && $.isFunction(resolveValues[i].promise)) {
resolveValues[i].promise()
.done(updateFn(i, resolveContexts, resolveValues))
.fail(deferred.reject)
.progress(updateFn(i, progressContexts, progressValues))
} else {
--remain;
}
}
}
if (!remain) deferred.resolveWith(resolveContexts, resolveValues)
return deferred.promise();
复制代码
首先赋值,progressValues
、progressContexts
、resolveContexts
这些都赋值为传入参数的长度的数组。然后遍历传入的参数,判断是不是异步对象,如果是就调用对应的方法。如果不是异步对象,则remain减一。最后remain的时候,调用resolveWith方法。最后返回的是一个异步对象。