阅读 88

zepto源码deferred模块学习

zeptoDeferred模块是用于处理异步的一个模块,不得不说这些人真的厉害,刚开始哪有什么谷歌,百度可以用。前天看到一个沸点,就是谷歌的工程师有多厉害:就是他们在造谷歌的时候,并没有前人可以借鉴的经验。

结构

;(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对象,deferredpromise进行扩展,除了有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的状态有pendingresolved已完成,reject被拒绝。然后遍历tuples, list$.Callbacks工厂方法生成的管理回调函数的一系列方法,stateString就表示当前的状态,可以看到只有前两项有状态描述。然后在promise上添加donefailprogress方法,这些方法实质是往回调列表中添加回调函数。

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/resolveWithreject/rejectWithnotify/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的回调,则调用deferrednotifyWith方法。否则就是remain减少1,如果全部回调都完成了,就执行deferredresolveWith方法。
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();
复制代码

首先赋值,progressValuesprogressContextsresolveContexts这些都赋值为传入参数的长度的数组。然后遍历传入的参数,判断是不是异步对象,如果是就调用对应的方法。如果不是异步对象,则remain减一。最后remain的时候,调用resolveWith方法。最后返回的是一个异步对象。

关注下面的标签,发现更多相似文章
评论