5-4 $.Deferred的设计
本节编程练习不计算学习进度,请电脑登录imooc.com操作

$.Deferred的设计

由于1.7版本后$.Callbacks从Deferred中抽离出去了,目前版本的Deferred.js代码不过150行,而真正$.Deferred的实现只有100行左右,实现的逻辑是相当犀利的。

因为Callback被剥离出去后,整个Deferred就显得非常的精简,代码直接通过extend扩展到静态接口上,对于extend的继承这个东东,在之前就提及过jQuery如何处理内部jQuery与init相互引用this的问题,所以当jQuery.extend只有一个参数的时候,其实就是对jQuery静态方法的一个扩展。

jQuery.extend({
   Deferred:function(func){
        ...省略代码....
        return deferred
   },
   when:function(func){
      ...省略代码....
      return deferred.promise();
   }
})

我们来具体看看2个静态方法内部都干了些什么?

Deferred整体结构:右边代码所示。

Deferred就是一个简单的工厂方法,有两种方式使用:

var a = $.Deferred()
$.Deferred(function(){})

内部其实是严重依赖$.Callbacks对象,Callbacks就是用来储存deferred依赖的数据的。

因为done、fail、progress就是jQuery.Callbacks("once memory")所有对应的处理:

var list = jQuery.Callbacks("once memory")
promise['done'] = list.add;

deferred定义了一系列的接口,堪称一绝,100多行的代码,精练的有些过分。

Deferred方法内部建议了2个对象,一个是deferred外部接口对象,一个是内部promise对象。

promise对象解释是一个受限的对象, 这就是所谓的受限制的deferred对象,因为相比之前, 返回的deferred不再拥有resolve(With), reject(With), notify(With)这些能改变deferred对象状态并且执行callbacklist的方法了,只能是then、done、fali等方法。

其内部通过tuples数组,存储了所有的接口API,通过遍历把所有的接口一次都挂到内部promise与deferred对象上。

其中定义了done、fail以及progress这几个方法,其实就是Callbacks回调函数中的add方法,用与push外部的的数据,保存在队列上。

我们通过resolve、reject以及notify其实也就是处理Callbacks中的队列列表。

任务

  1. <!DOCTYPE HTML>
  2. <html>
  3. <head>
  4. <meta charset="utf-8">
  5. <script src="http://code.jquery.com/jquery-latest.js"></script>
  6. <title></title>
  7. </head>
  8. <body>
  9.  
  10. <script type="text/javascript">
  11.  
  12.  
  13. // jQuery. Deferred主要处理:
  14. // 显而易见Deferred是个工厂类,返回的是内部构建的deferred对象
  15. // tuples 创建三个$.Callbacks对象,分别表示成功,失败,处理中三种状态
  16. // 创建了一个promise对象,具有state、always、then、primise方法
  17. // 扩展primise对象生成最终的Deferred对象,返回该对象
  18. // primise对象就是一个受限对象,只读
  19. var Deferred = function(func) {
  20. var tuples = [
  21. //1 动作
  22. //2 侦听器
  23. //3 最终状态
  24. //后面的操作将是围绕这些接口处理
  25. ["resolve", "done", jQuery.Callbacks("once memory"), "resolved"],
  26. ["reject", "fail", jQuery.Callbacks("once memory"), "rejected"],
  27. ["notify", "progress", jQuery.Callbacks("memory")]
  28. ],
  29. state = "pending",
  30. //扩展的primise对象
  31. promise = {
  32. state: function() {},
  33. always: function() {},
  34. then: function( /* fnDone, fnFail, fnProgress */ ) {},
  35. promise: function(obj) {}
  36. },
  37. deferred = {};
  38. //定义管道风格的接口pipe
  39. promise.pipe = promise.then;
  40. //逐个添加所有的接口到deferred对象上
  41. jQuery.each(tuples, function(i, tuple) {
  42. deferred[tuple[0]] = function() {
  43. deferred[tuple[0] + "With"](this === deferred ? promise : this, arguments);
  44. return this;
  45. };
  46. deferred[tuple[0] + "With"] = list.fireWith;
  47. });
  48. //转成成promise对象
  49. promise.promise(deferred);
  50. //如果传递的参数是函数,直接运行
  51. if (func) {
  52. func.call(deferred, deferred);
  53. }
  54. return deferred;
  55. }
  56.  
  57. //when就是一个合集的处理
  58. //可以收集多个异步操作,合并成功后处理
  59. //同时也可以绑定Promise 对象的其它方法,如 defered.then
  60. //所以when内部必须要创建一个deferred对象
  61. var when = function(subordinate /* , ..., subordinateN */ ) {
  62. var i = 0,
  63. resolveValues = slice.call(arguments),
  64. length = resolveValues.length,
  65. deferred = remaining === 1 ? subordinate : jQuery.Deferred(),
  66. updateFunc = function(i, contexts, values) {
  67. return function(value) {};
  68. },
  69. progressValues, progressContexts, resolveContexts;
  70. if (length > 1) {
  71. progressValues = new Array(length);
  72. progressContexts = new Array(length);
  73. resolveContexts = new Array(length);
  74. for (; i < length; i++) {
  75. if (resolveValues[i] && jQuery.isFunction(resolveValues[i].promise)) {
  76. resolveValues[i].promise()
  77. .done(updateFunc(i, resolveContexts, resolveValues))
  78. .fail(deferred.reject)
  79. .progress(updateFunc(i, progressContexts, progressValues));
  80. } else {
  81. --remaining;
  82. }
  83. }
  84. }
  85. return deferred.promise();
  86. }
  87.  
  88. </script>
  89.  
  90. </body>
  91. </html>
下一节