我的编程空间,编程开发者的网络收藏夹
学习永远不晚

如何用jQuery 2.0.3源码分析Deferred

短信预约 -IT技能 免费直播动态提醒
省份

北京

  • 北京
  • 上海
  • 天津
  • 重庆
  • 河北
  • 山东
  • 辽宁
  • 黑龙江
  • 吉林
  • 甘肃
  • 青海
  • 河南
  • 江苏
  • 湖北
  • 湖南
  • 江西
  • 浙江
  • 广东
  • 云南
  • 福建
  • 海南
  • 山西
  • 四川
  • 陕西
  • 贵州
  • 安徽
  • 广西
  • 内蒙
  • 西藏
  • 新疆
  • 宁夏
  • 兵团
手机号立即预约

请填写图片验证码后获取短信验证码

看不清楚,换张图片

免费获取短信验证码

如何用jQuery 2.0.3源码分析Deferred

本篇文章给大家分享的是有关如何用jQuery 2.0.3源码分析Deferred,小编觉得挺实用的,因此分享给大家学习,希望大家阅读完这篇文章后可以有所收获,话不多说,跟着小编一起来看看吧。

构建Deferred对象时候的流程图

如何用jQuery 2.0.3源码分析Deferred

**********************源码解析**********************

因为callback被剥离出去后,整个deferred就显得非常的精简

jQuery.extend({        Deferred : function(){}            when : function()   )}

对于extend的继承这个东东,在之前就提及过jquery如何处理内部jquery与init相互引用this的问题

对于JQ的整体架构一定要弄懂 http://www.cnblogs.com/aaronjs/p/3278578.html

所以当jQuery.extend只有一个参数的时候,其实就是对jQuery静态方法的一个扩展

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

Deferred整体结构:

源码精简了部分代码

Deferred: function( func ) {          var tuples = [                  // action, add listener, listener list, final state                  [ "resolve", "done", jQuery.Callbacks("once memory"), "resolved" ],                  [ "reject", "fail", jQuery.Callbacks("once memory"), "rejected" ],                  [ "notify", "progress", jQuery.Callbacks("memory") ]              ],              state = "pending",              promise = {                  state: function() {},                  always: function() {},                  then: function(  ) { },                  // Get a promise for this deferred                  // If obj is provided, the promise aspect is added to the object                  promise: function( obj ) {}              },              deferred = {};          jQuery.each( tuples, function( i, tuple ) {              deferred[ tuple[0] + "With" ] = list.fireWith;          });          promise.promise( deferred );          // All done!          return deferred;      },
  1. 显而易见Deferred是个工厂类,返回的是内部构建的deferred对象

  2. tuples 创建三个$.Callbacks对象,分别表示成功,失败,处理中三种状态

  3. 创建了一个promise对象,具有state、always、then、primise方法

  4. 扩展primise对象生成最终的Deferred对象,返回该对象

这里其实就是3个处理,但是有个优化代码的地方,就是把共性的代码给抽象出来,通过动态生成了

具体源码分析:

Deferred自身则围绕这三个对象进行更高层次的抽象

  • 触发回调函数列表执行(函数名)

  • 添加回调函数(函数名)

  • 回调函数列表(jQuery.Callbacks对象)

  • deferred最终状态(第三组数据除外)

var tuples = [          // action, add listener, listener list, final state          [ "resolve", "done", jQuery.Callbacks("once memory"), "resolved" ],          [ "reject", "fail", jQuery.Callbacks("once memory"), "rejected" ],          [ "notify", "progress", jQuery.Callbacks("memory") ]      ],

这里抽象出2组阵营:

1组:回调方法/事件订阅  

done,fail,progress

2组:通知方法/事件发布    

resolve,reject,notify,resolveWith,rejectWith,notifyWith

tuples 元素集 其实是把相同有共同特性的代码的给合并成一种结构,然后通过一次处理

jQuery.each( tuples, function( i, tuple ) {              var list = tuple[ 2 ],                  stateString = tuple[ 3 ];              promise[ tuple[1] ] = list.add;              if ( stateString ) {                  list.add(function() {                      state = stateString;                   // [ reject_list | resolve_list ].disable; progress_list.lock                  }, 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;          });

对于tuples的3条数据集是分2部分处理的

第一部分将回调函数存入

promise[ tuple[1] ] = list.add;

其实就是给promise赋予3个回调函数

promise.done = $.Callbacks("once memory").add
promise.fail = $.Callbacks("once memory").add
promise.progressl = $.Callbacks("memory").add

如果存在deferred最终状态

默认会预先向doneList,failList中的list添加三个回调函数

if ( stateString ) {      list.add(function() {          // state = [ resolved | rejected ]          state = stateString;       // [ reject_list | resolve_list ].disable; progress_list.lock      }, tuples[ i ^ 1 ][ 2 ].disable, tuples[ 2 ][ 2 ].lock );  }

*************************************************************
这里有个小技巧

i ^ 1 按位异或运算符

所以实际上第二个传参数是1、0索引对调了,所以取值是failList.disable与doneList.disable

*************************************************************

通过stateString有值这个条件,预先向doneList,failList中的list添加三个回调函数

分别是:

doneList : [changeState, failList.disable, processList.lock]  failList : [changeState, doneList.disable, processList.lock]
  • changeState 改变状态的匿名函数,deferred的状态,分为三种:pending(初始状态), resolved(解决状态), rejected(拒绝状态)

  • 不论deferred对象最终是resolve(还是reject),在首先改变对象状态之后,都会disable另一个函数列表failList(或者doneList)

  • 然后lock processList保持其状态,最后执行剩下的之前done(或者fail)进来的回调函数

所以第一步最终都是围绕这add方法

  • done/fail/是list.add也就是callbacks.add,将回调函数存入回调对象中

第二部分很简单,给deferred对象扩充6个方法

  • resolve/reject/notify 是 callbacks.fireWith,执行回调函数

  • resolveWith/rejectWith/notifyWith 是 callbacks.fireWith 队列方法引用

最后合并promise到deferred

promise.promise( deferred );  jQuery.extend( obj, promise )

所以最终通过工厂方法Deferred构建的异步对象带的所有的方法了

return 内部的deferred对象了

如何用jQuery 2.0.3源码分析Deferred

由此可见我们在

var defer = $.Deferred(); //构建异步对象

的时候,内部的对象就有了4个属性方法了

  1. deferred: Object

    1. always: function () {

    2. done: function () {

    3. fail: function () {

    4. notify: function () {

    5. notifyWith: function ( context, args ) {

    6. pipe: function ( ) {

    7. progress: function () {

    8. promise: function ( obj ) {

    9. reject: function () {

    10. rejectWith: function ( context, args ) {

    11. resolve: function () {

    12. resolveWith: function ( context, args ) {

    13. state: function () {

    14. then: function ( ) {

  2. promise: Object

    1. always: function () {

    2. done: function () {

    3. fail: function () {

    4. pipe: function ( ) {

    5. progress: function () {

    6. promise: function ( obj ) {

    7. state: function () {

    8. then: function ( ) {

  3. state: "pending"

  4. tuples: Array[3]

构造图

如何用jQuery 2.0.3源码分析Deferred

以上只是在初始化构建的时候,我们往下看看动态执行时候的处理

*****************执行期***********************

一个最简单的demo为例子

var d = $.Deferred();    setTimeout(function(){          d.resolve(22)    },0);    d.then(function(val){        console.log(val);   })

当延迟对象被 resolved 时,任何通过 deferred.then或deferred.done 添加的 doneCallbacks,都会被调用。回调函数的执行顺序和它们被添加的顺序是一样的。传递给 deferred.resolve() 的 args 参数,会传给每个回调函数。当延迟对象进入 resolved 状态后,再添加的任何 doneCallbacks,当它们被添加时,就会被立刻执行,并带上传入给 .resolve()的参数

换句话说,我们调用d.resolve(22) 就等于是调用。

匿名函数并传入参数值 22

function(val){        console.log(val); //22   }

当前实际的使用中会有各种复杂的组合情况,但是整的外部调用流程就是这样的

***************** resolve的实现 *******************

我们回顾下,其实Deferred对象,内部的实现还是Callbacks对象,只是在外面再封装了一层API,供接口调用

d.resolve(22)

实际上调用的就是通过这个代码生成的

deferred[ tuple[0] ] = function() {      deferred[ tuple[0] + "With" ]( this === deferred ? promise : this, arguments );      return this;  };   deferred[ tuple[0] + "With" ] = list.fireWith;
deferred.resolveWith()

最终执行的就是 list.fireWith

callbacks.fireWith()

所以最终又回到回调对象callbacks中的私有方法fire()了

Callbacks会通过

callbacks.add()

把回调函数给注册到内部的list = []上,我们回来过看看

deferred.then()

d.then(function(val){        console.log(val);   })

***************** then的实现 *******************

then: function(  ) {      var fns = arguments;      return jQuery.Deferred(function( newDefer ) {          jQuery.each( tuples, function( i, tuple ) {              var action = tuple[ 0 ],                  fn = jQuery.isFunction( fns[ i ] ) && fns[ i ];              // deferred[ done | fail | progress ] for forwarding actions to newDefer              deferred[ tuple[1] ](function() {                     //省略............              });          });          fns = null;      }).promise();  },
  • 递归jQuery.Deferred

  • 传递了func

  • 链式调用了promise()

因为在异步对象的方法都是嵌套找作用域属性方法的

这里我额外的提及一下作用域

var d = $.Deferred();

这个异步对象d是作用域是如何呢?

第一层:无可争议,浏览器环境下最外层是 window

第二层:jquery本身是一个闭包

第三层: Deferred工厂方法产生的作用域

如果用d.then()方法呢?

很明显then方法又是嵌套在内部的函数,所以执行的时候都默认会包含以上三层作用域+自己本身函数产生的作用域了

我们用个简单图描绘下

如何用jQuery 2.0.3源码分析Deferred

根据规则,在最内部的函数能够访问上层作用域的所有的变量

我们先从使用的层面去考虑下结构设计:

demo 1

var defer = $.Deferred();     var filtered = defer.then(function( value ) {          return value * 2;        });     defer.resolve( 5 );     filtered.done(function( value ) {        console.log(value) //10    });

demo 2

var defer = $.Deferred();     defer.then(function(value) {      return value * 2;    }).then(function(value) {      return value * 2;    }).done(function(value) {        alert(value)  //20    });     defer.resolve( 5 );

其实这里就是涉及到defer.then().then().done()  链式调用了

API是这么定义的:

deferred.then( doneFilter [, failFilter ] [, progressFilter ] )

从jQuery 1.8开始, 方法返回一个新的promise(承诺),通过一个函数,可以过滤deferred(延迟)的状态和值。替换现在过时的deferred.pipe()方法。 doneFilter 和 failFilter函数过滤原deferred(延迟)的解决/拒绝的状态和值。 progressFilter 函数过滤器的任何调用到原有的deferred(延迟)的notify 和 notifyWith的方法。 这些过滤器函数可以返回一个新的值传递给的 promise(承诺)的.done() 或 .fail() 回调,或他们可以返回另一个观察的对象(递延,承诺等)传递给它的解决/拒绝的状态和值promise(承诺)的回调。 如果过滤函数是空,或没有指定,promise(承诺)将得到与原来值相同解决(resolved)或拒绝(rejected)。

我们抓住几点:

  • 返回的是新的promise对象

  • 内部有一个滤器函数

从demo 1中我们就能看到

经过x.then()方法处理的代码中返回的this(filtered ),不是原来的$.Deferred()所有产生的那个异步对象(defer )了

所以,每经过一个then那么内部处理的this都要被重新设置,那么为什么要这样处理呢?

源码

then: function(  ) {                      var fns = arguments;                      //分别为deferred的三个callbacklist添加回调函数,根据fn的是否是函数,分为两种情况                      return jQuery.Deferred(function( newDefer ) {                          jQuery.each( tuples, function( i, tuple ) {                              var action = tuple[ 0 ],                                  fn = jQuery.isFunction( fns[ i ] ) && fns[ i ];                              // deferred[ done | fail | progress ] for forwarding actions to newDefer                              deferred[ tuple[1] ](function() {                                  var returned = fn && fn.apply( this, arguments );                                  if ( returned && jQuery.isFunction( returned.promise ) ) {                                      returned.promise()                                          .done( newDefer.resolve )                                          .fail( newDefer.reject )                                          .progress( newDefer.notify );                                  } else {                                      newDefer[ action + "With" ]( this === promise ? newDefer.promise() : this, fn ? [ returned ] : arguments );                                  }                              });                          });                          fns = null;                      }).promise();                  },

在Deferred传递实参的时候,支持一个flag,jQuery.Deferred(func)

传递一个回调函数

// Call given func if any  if ( func ) {      func.call( deferred, deferred );  }

所以newDefer可以看作是

newDefer = $.Deferred();

那么func回调的处理的就是过滤函数了

deferred[ tuple[1] ](function() {      var returned = fn && fn.apply( this, arguments );      if ( returned && jQuery.isFunction( returned.promise ) ) {          returned.promise()              .done( newDefer.resolve )              .fail( newDefer.reject )              .progress( newDefer.notify );      } else {          newDefer[ action + "With" ]( this === promise ? newDefer.promise() : this, fn ? [ returned ] : arguments );      }  });

这里其实也有编译函数的概念,讲未来要执行的代码,预先通过闭包函数也保存起来,使其访问各自的作用域

第一步

分解tuples元素集

jQuery.each( tuples, function( i, tuple ) {     //过滤函数第一步处理  })

第二步

分别为deferred[ done | fail | progress ]执行对应的add方法,增加过滤函数给done | fail | progress 方法

deferred[ tuple[1] ](  传入过滤函数  )//过滤函数 执行的时候在分解

代码即

deferred[done] = list.add = callback.add

第三步

返回return jQuery.Deferred().promise()

此时构建了一个新的Deferred对象,但是返回的的是经过promise()方法处理后的,返回的是一个受限的promise对象

所以整个then方法就处理了2个事情

  • 构建一个新的deferred对象,返回受限的promise对象

  • 给父deferred对象的[ done | fail | progress ]方法都增加一个过滤函数的方法

我们知道defer.then方法返回的是一个新的jQuery.Deferred().promise()对象

那么我们把defer.then返回的称之为子对象,那么如何与父对象var defer = $.Deferred() 关联的起来的

我看看源码

deferred[ tuple[1] ](//过滤函数//)

deferred其实就是根级父对象的引用,所以就嵌套再深,其实都是调用了父对象deferred[ done | fail | progress 执行add罢了

如何用jQuery 2.0.3源码分析Deferred

从图中就能很明显的看到 2个不同的deferred对象中 done fail progress分别都保存了不同的处理回调了

  deferred.resolve( args )

  • 当延迟对象被 resolved 时,任何通过 deferred.then或deferred.done 添加的 doneCallbacks,都会被调用

  • 回调函数的执行顺序和它们被添加的顺序是一样的

  • 传递给 deferred.resolve() 的 args 参数,会传给每个回调函数

  • 当延迟对象进入 resolved 状态后,再添加的任何 doneCallbacks,当它们被添加时,就会被立刻执行,并带上传入给.resolve()的参数

流程如图

如何用jQuery 2.0.3源码分析Deferred 

流程解析:

1 执行fire()方法,递归执行list所有包含的处理方法

2 执行了默认的 changeState, disable, lock 方法、

3 执行过滤函数

根据 var returned = fn.apply( this, arguments )的返回值(称作returnReferred)是否是deferred对象

  • 返回值是deferred对象,那么在returnReferred对象的三个回调函数列表中添加newDeferred的resolve(reject,notify)方法,也就是说newDeferrred的执行依赖returnDeferred的状态

  • 不是函数的情况(如值为undefined或者null等),直接链接到newDeferred的resolve(reject,notify) 方法,也就是说  newDeferrred的执行依赖外层的调用者deferred的状态或者说是执行动作(resolve还是reject或者是notify)  此时deferred.then()相当于将自己的callbacklist和newDeferred的callbacklist连接起来

如何用jQuery 2.0.3源码分析Deferred

下面就是嵌套deferred对象的划分了

如何用jQuery 2.0.3源码分析Deferred

以上就是如何用jQuery 2.0.3源码分析Deferred,小编相信有部分知识点可能是我们日常工作会见到或用到的。希望你能通过这篇文章学到更多知识。更多详情敬请关注编程网行业资讯频道。

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

如何用jQuery 2.0.3源码分析Deferred

下载Word文档到电脑,方便收藏和打印~

下载Word文档

猜你喜欢

jquery源码如何解析符号

jQuery源码是一个相当庞大和复杂的项目,包含数千行JavaScript代码和许多符号,这使得解析它变得非常困难。 在本文中,我们将探讨一些方法和技巧,可以用来更好地解析jQuery源码中的符号。首先,我们需要了解一些基本的JavaScript符号和概念。在JavaScript中,符号是一些具体的字符或字符串,它们被用来执行特定的操作或表示特定的值。例如,等号(=)用于分配值
2023-05-18

如何用源代码分析FileZilla

这期内容当中小编将会给大家带来有关如何用源代码分析FileZilla,文章内容丰富且以专业的角度为大家分析和叙述,阅读完这篇文章希望大家可以有所收获。FileZilla是一种快速、可信赖的FTP客户端以及服务器端开放源代码程式,具有多种特色
2023-06-16

如何用源码分析Java HashMap实例

本篇文章为大家展示了如何用源码分析Java HashMap实例,内容简明扼要并且容易理解,绝对能使你眼前一亮,通过这篇文章的详细介绍希望你能有所收获。引言HashMap在键值对存储中被经常使用,那么它到底是如何实现键值存储的呢?一 Entr
2023-06-17

如何分析Linux内核源码do_fork

本篇文章为大家展示了如何分析Linux内核源码do_fork,内容简明扼要并且容易理解,绝对能使你眼前一亮,通过这篇文章的详细介绍希望你能有所收获。我们都知道进程是Linux内核中最为重要的一个抽象概念,那么我们平时在fork一个进程时,该
2023-06-16

如何用源码分析在linux上的safe point

这篇文章将为大家详细讲解有关如何用源码分析在linux上的safe point,文章内容质量较高,因此小编分享给大家做个参考,希望大家阅读完这篇文章后对相关知识有一定的了解。safe point 顾明思意,就是安全点,当需要jvm做一些操作
2023-06-17

如何进行FileZilla的源代码分析

这篇文章将为大家详细讲解有关如何进行FileZilla的源代码分析,文章内容质量较高,因此小编分享给大家做个参考,希望大家阅读完这篇文章后对相关知识有一定的了解。FileZilla是一种快速、可信赖的FTP客户端以及服务器端开放源代码程式,
2023-06-16

如何用Play源代码分析Server启动过程

这期内容当中小编将会给大家带来有关如何用Play源代码分析Server启动过程,文章内容丰富且以专业的角度为大家分析和叙述,阅读完这篇文章希望大家可以有所收获。Play是个Rails风格的Java Web框架。如何调试请看此处。以下进入正题
2023-06-17

源码分析C++是如何实现string的

我们平时使用C++开发过程中或多或少都会使用std::string,但您了解string具体是如何实现的吗,本文小编就带大家从源码角度分析一下
2023-05-14

如何用源码分析Struts2请求处理及过程

这期内容当中小编将会给大家带来有关如何用源码分析Struts2请求处理及过程,文章内容丰富且以专业的角度为大家分析和叙述,阅读完这篇文章希望大家可以有所收获。1.1 Struts2请求处理1. 一个请求在Struts2框架中的处理步骤:a)
2023-06-17

如何进行Netlink源码及实例的分析

本篇文章给大家分享的是有关如何进行Netlink源码及实例的分析,小编觉得挺实用的,因此分享给大家学习,希望大家阅读完这篇文章后可以有所收获,话不多说,跟着小编一起来看看吧。前言这几天在看 ipvs 相关代码的时候又遇到了 netlink
2023-06-15

如何进行HashMap扩容机制源码分析

这期内容当中小编将会给大家带来有关如何进行HashMap扩容机制源码分析,文章内容丰富且以专业的角度为大家分析和叙述,阅读完这篇文章希望大家可以有所收获。具体看源码之前,我们先简单的说一下HashMap的底层数据结构  1、HashMap底
2023-06-02

编程热搜

目录