jQuery异步框架探究1:jQuery._Deferred方法
jQuery异步框架应用于jQuery数据缓存模块、jQuery ajax模块、jQuery事件绑定模块等多个模块,是jQuery的基础功能之中的一个。实际上jQuery实现的异步回调机制能够看做java nio(不是aio)的近似。所以须要从更抽象层面的"异步回调"的视角分析解读该模块。这个部分与dom功能关系不大,是独立部分,能够看作是jQuery工具系列之中的一个。
与异步框架相关的方法定义于jQuery类的静态方法中。
仅仅有三个方法,可是功能和应用及其强大!本篇具体解说第一个方法jQuery._Deferred()。
- _Deferred: function() {
- var // callbacks list
- callbacks = [],
- // stored [ context , args ]
- fired,
- // to avoid firing when already doing so
- firing,
- // flag to know if the deferred has been cancelled
- cancelled,
- // the deferred itself
- deferred = {
- // done( f1, f2, ...)
- done: function() {
- if ( !cancelled ) {
- var args = arguments,
- i,
- length,
- elem,
- type,
- _fired;
- if ( fired ) {
- _fired = fired;
- fired = 0;
- }
- for ( i = 0, length = args.length; i < length; i++ ) {
- elem = args[ i ];
- type = jQuery.type( elem );
- if ( type === "array" ) {
- deferred.done.apply( deferred, elem );
- } else if ( type === "function" ) {
- callbacks.push( elem );
- }
- }
- if ( _fired ) {
- deferred.resolveWith( _fired[ 0 ], _fired[ 1 ] );
- }
- }
- return this;
- },
- // resolve with given context and args
- resolveWith: function( context, args ) {
- if ( !cancelled && !fired && !firing ) {
- // make sure args are available (#8421)
- args = args || [];
- firing = 1;
- try {
- while( callbacks[ 0 ] ) {
- callbacks.shift().apply( context, args );
- }
- }
- finally {
- fired = [ context, args ];
- firing = 0;
- }
- }
- return this;
- },
- // resolve with this as context and given arguments
- resolve: function() {
- deferred.resolveWith( this, arguments );
- return this;
- },
- // Has this deferred been resolved?
- isResolved: function() {
- return !!( firing || fired );
- },
- // Cancel
- cancel: function() {
- cancelled = 1;
- callbacks = [];
- return this;
- }
- };
- return deferred;
- },
这是jQuery异步框架最基础的一个函数。真正对外暴露的Deferred函数依赖该函数的实现。从其名字能够看出。jQuery的设计者并不想使用者直接调用该方法,尽管这种方法已经非常灵活已经能够应用到非常多场景了,并且javascript语言也没有语言层面上的机制阻止用户真的调用该方法。
分析其内部实现能够发现该函数设计为在被调用时并不须要传入不论什么參数,而是直接调用后得到一个异步对象:var deferred = jQuery._Deferred()。重点是这个返回的异步对象,这个对象有五个方法:done、resolveWith、resolve、isResolved、cancel,每一个方法都值得深入分析。当然_Deferred函数里面还有四个变量:callbacks、fired、firing、cancelled。这四个变量也值得深入分析。
1 done方法
这种方法的实现是把传入的參数push到callbacks空数组中。以便兴许resolveWith方法使用callbacks数组中的函数。
这种方法的參数仅仅接受两种类型:要么是函数。要么是函数数组,其它类型不被处理。且传入函数数组时递归调用done方法自身继续把数组中的函数元素push到callbacks数组中。
配合resolveWith方法能够知道done方法的主要功能是存储将要运行的函数集合,resolveWith方法的功能是在指定的对象上依据指定的參数对象依次调用这个函数集合。因此,callbacks函数集合相当于是回调处理器集合,fired = [ context, args ]相当于是调用对象(包含其參数)。
这样分离后带来的灵活性是惊人的--异步处理框架即将成型。
顺便说一下,这种方法的名字done很的不友好,对于熟悉回调模式的朋友们来说。事实上这种方法名应该是addHandlers或者addCallbacks,甚至返回对象deferred(延迟)的名字都不如asynchor来得直观。
这种方法最后返回thiskeyword,即deferred对象自身,这样做的优点是能够链式多次调用,比方jQuery._Deferred().done(f1,f2).done([f3,f4,f5]),假设deferred对象的其它方法也这么干的话。那么也能够链式调用其它方法,而其它方法正是这么干的。
关于_fired变量和相关代码的作用在分析完resolveWith方法之后再看才会明朗。
2 resolveWith方法
resolveWith方法的实现是迭代回调函数集合callbacks中的函数于指定对象context和參数args之上运行--假设callbacks不为空的话,而且最后总是把指定对象context和參数args存入fired变量中--有可能还没有运行回调,假设callbacks为空。
resolveWith方法先推断回调函数集合callbacks是否有值"callbacks[ 0 ]"。没有的话什么也不触发。
resolveWith方法最核心的是这一行:callbacks.shift().apply( context, args )。
除了从回调函数集合callbacks中取出第一个函数元素调用外它还移除了这个元素,改变了callbacks数组,这样保证回调函数集合callbacks中的每一个元素仅能运行唯一一次。
对于军事迷来说,有一个模型与这里的_Deferred函数极为类似,差点儿再也找不到更贴切的类比了--手枪。
纵观_Deferred函数,回调函数数组callbacks相当于弹夹;
传递给done方法的函数參数相当于将要存储于弹夹的子弹,done方法主要职责是提供"压弹入夹"操作;
传递给resolveWith方法的參数( context, args )相当于手枪,resolveWith方法主要职责是运行"开枪"操作;
fired变量相当于枪套。存储传递给resolveWith方法的手枪[ context, args ];
如今回过头再看done方法,done方法是与resolveWith配合使用的,普通情况下既能够先上弹后开枪--先调用done方法后调用resolveWith方法,这样的场景下手枪存在前就已经备有子弹了,因此直接在调用resolveWith方法时触发"开枪"操作。可是也能够先备枪后上弹射击--先调用resolveWith方法后调用done方法,这样的场景在传递手枪的时候还没有子弹,提供子弹后才去触发射击动作--done方法内部调用resolveWith方法:
- if ( fired ) {
- _fired = fired;
- fired = 0;
- }
- <span style="white-space:pre"> </span>// ......
- if ( _fired ) {
- deferred.resolveWith( _fired[ 0 ], _fired[ 1 ] );
- }
除了前面两种操作外另一种特殊操作:上弹-->开枪-->再上弹开枪(先调用done方法再调用resolveWith方法后再调用done方法),这个时候done方法装上新弹(传入新的函数參数push到回调函数数组)后可反复开枪(继续内部调用resolveWith方法),假设不换上新弹(不传入新的函数參数)就不能反复开枪(原回调函数数组经过resolveWith方法调用一次后已清空,它不可反复使用),这个操作时序完美的模拟手枪空仓挂机复位射击功能(子弹打光时阻止套筒回位,更换弹夹后可高速推弹入膛)!
这行代码"fired = 0;"的作用很重要,正由于有这行代码才干够在done方法调用resolveWith方法反复开枪时生效(由于resolveWith方法内的第二个推断条件"if ( !cancelled && !fired && !firing )",我们前面说过fired变量表示枪套。怎么理解呢?关于枪套变量fired也须要重点解释。參见以下详解fired的部分。
我们再来看一个扩展的操作:jQuery._Deferred().done(f1,f2).resolveWith(context1, args1).done(f3).done([f4]).done(f5,[f6,f7]),说明空仓挂机复位射击的确是可反复的!这个世界上应该没有哪位枪械师会设计不能反复使用或仅仅能反复使用一次的手枪吧?
关于这两个方法总结一下针对同一把枪的用法:
jQuery._Deferred().done(f1,f2).resolveWith(context1, args1)--先上弹后造枪,击发一次。
jQuery._Deferred().resolveWith(context1, args1).done(f1,f2)--先造枪后上弹,击发一次。
jQuery._Deferred().done(f1,f2).resolveWith(context1, args1).done(f3,f4).done([f5,f6],f7)--先上弹后造枪射击一次,再空仓挂机复位射击两次(可反复很多其它次射击)。
关于这两个方法即使做了总结之后也并没有完,注意前面的一个提示性描写叙述:针对同一把枪,我们已经知道同一把枪能够反复使用不同的子弹,可是假设想针对不同的枪使用同样的子弹呢?这个需求是合理的--成员国众多的北约内部,标准9mm巴拉姆弹可不是仅仅能应用于沙漠之鹰。
"针对不同的枪"说明须要调用resolveWith方法至少两次。第一次调用resolveWith方法传入的參数(context1, args1)代表第一把枪,第二次传入的參数(context2, args2)代表第二把枪。我们看看会发生什么情况:"jQuery._Deferred().done(f1,f2).resolveWith(context1, args1).resolveWith(context2,args2);"非常遗憾。这个调用会歇菜!
还是由于枪套变量fired的原因。
变通一下呢:"jQuery._Deferred().done(f1,f2).resolveWith(context1,
args1).done(f3).resolveWith(context2, args2)",还是不行。相同由于枪套变量fired的原因。resolveWith方法能且仅能调用一次,它不像done一样能够链式调用多次。
除了由于枪套变量fired的原因外,即使排除这个原因,"不同的枪使用同样的子弹"也是不能实现的。枪对子弹是一对多的关系。子弹对枪却是一对一的关系,即回调函数数组中的每一颗子弹被一仅仅手枪击发后并不能被还有一仅仅枪再次击发,真正合理的需求描写叙述是"不同型号的枪支应该能够使用同样规格的子弹"。
所以结论是--jQuery._Deferred()不能针对同样的子弹使用不同的手枪,它每次上弹后就觉得仅仅能匹配给第一次传入的手枪(或者说第一次造枪后就仅仅能使用这样的型号的这仅仅手枪),假设要使用不同的手枪怎么办呢?--多次调用jQuery._Deferred()方法就可以。
3 枪套--变量fired
首先须要熟悉的一个基础知识点是:done方法和resolveWith方法操作的变量fired是在函数调用对象链上一级的调用对象中,其它三个变量都是如此。这意味着deferred对象的不同方法能够共同操作它们。
resolveWith方法内有一行推断语句:"if ( !cancelled && !fired && !firing )",这三个变量不论什么一个都可能导致不能"开枪",关于firing和cancelled參见以下具体分析。这里分析fired变量。假设这个枪套不是有枪才干运行开枪操作,或者简单说仅仅有这个枪套是空的才干运行开枪(事实上同一时候受firing和cancelled约束):枪套不空说明里面存储手枪了。手枪没取出来当然不能开枪--实际上即使此时传入新的手枪进来也不能使用(jQuery._Deferred()对象比較重感情,它仅仅使用第一次接触到的手枪,这也充分说明第一次是多么的重要。)。resolveWith方法在finally块中赋值给变量:"fired
= [ context, args ];",相当于使用结束后将手枪插入手枪套,
再看done方法对fired变量的操作:"if ( fired ) {_fired = fired;fired = 0;}"。枪套中有枪时,把它取出来存入当前调用对象域中的暂时变量_fired。此时一定要清空枪套!
否则以下调用resolveWith方法会失效(resolveWith方法会先检查枪套情况)。
4 射击指示器--变量firing
这个变量相当于"射击指示器",作用是当前正在射击时(调用resolveWith方法并通过了条件推断)给手枪标记一个状态--"正在射击"的状态("firing = 1;")。这个状态下的手枪不能再次扣动扳机开枪(即不能再次调用resolveWith方法)。除非当前这次射击结束(在finally块中清除这个状态"firing = 0;")。
假设用手枪上的部件来类比的话,有点儿像"扳机连杆突起"。作用是一样的:击发前扳机连杆钩住阻铁能够运行当前射击,击发后扳机连杆脱钩阻铁,同一时候套筒在后座作用下通过"扳机连杆突起"强迫扳机连杆在射击过程中不能自己主动回位钩着阻铁,直到子弹击发出去后套筒复进到位本次射击结束才放开扳机连杆。这样就保证了射击过程中不会反复运行击发动作。当然并非全部手枪都通过设计"扳机连杆突起"完毕射击指示功能的。
这个变量在实际运行环境中应该过于严格了。除非javascript运行器採用的是多线程运行环境而且不同线程对这个变量的訪问可能出现race condition,否则我觉得单线程环境下应该不须要这个变量(也可能是我理解有误)。
5 全能保险--变量cancelled和cancel方法
cancelled变量很类似手枪上的"击发保险"+"弹夹防跌落保险",假设不是须要调用cancel方法显式设置的话,那么它也能够类似"击针保险",总之它是一个全能保险。
关闭保险之后("cancelled = 1;")。resolveWith方法无法开枪了(推断条件不通过),就像击发保险被锁死一样。
关闭保险之后("cancelled = 1;"),done方法无法压弹入匣了(推断条件不通过),就像弹夹扣被锁死一样。
调用cancel方法在关闭保险的同一时候也清空弹夹了("callbacks = [];")。
总之调用cancel方法之后整个系统都不好了,大部分地方都不能继续正常工作了。
6 resolve方法
这种方法调用resolveWith方法,不需多说。
7 isResolved方法
仅仅要射击指示器firing亮着或者枪套fired里有枪就觉得已resolved。
总之,假设你是一个对手枪模型工作原理十分清楚的军事迷,那么读懂jQuery._Deferred源代码和原理差点儿是天然的。jQuery._Deferred仅仅是jQuery异步框架的第一个最基础的部分就已经展开如此多的篇幅了,可是由于它是jQuery异步框架的核心,也是后两个方法的基础。这样的展开是值得的。
最后郑重申明一点:未经许可。严禁转载!尊重他人劳动成果是获得对等尊重的前提。
jQuery异步框架探究1:jQuery._Deferred方法的更多相关文章
- jQuery异步框架探究2:jQuery.Deferred方法
(本文针对jQuery1.6.1版本号)关于Deferred函数的描写叙述中有一个词是fledged,意为"羽翼丰满的",说明jQuery.Deferred函数应用应该更成熟. 这 ...
- jQuery原生框架中的jQuery.fn.extend和jQuery.extend
extend 方法在 jQuery 中是一个很重要的方法,jQuey 内部用它来扩展静态方法或实例方法,而且我们开发 jQuery 插件开发的时候也会用到它.但是在内部,是存在 jQuery.fn.e ...
- jQuery UI框架
jQuery UI框架 1.oschina开源社区-jQuery教程 2.jQuery PrimeUI(推荐) 3.弹出框.警告框.提示框.拖动支持.位置固定.选项卡切换 4.Bootstrap框架( ...
- jQuery验证框架 .
目录视图 摘要视图 订阅 “程序人生”中国软件开发者职业生涯调查 CSDN社区“三八节”特别活动 开发者职业生涯调查之未来 jQuery验证框架 分类: JQuery 2 ...
- jquery ajax异步提交表单数据的方法
使用jquery的ajax方法可以异步提交表单,成功后后台返回json数据,回调函数处理,可以不用刷新页面,达到异步的目的: 处理表单的数据可以用serialize()方法进行序列化,而如果提交的数据 ...
- JS常用方法总结,及jquery异步调用后台方法实例
//前台接收get参数值 function getQueryString(name) { var queryStrings = window.location.search.sp ...
- 使用反射机制实现jQuery调用ashx类中的指定方法
使用反射机制实现jQuery调用ashx类中的指定方法 近期用asp.net做个小网站,但又不喜欢使用asp.net的服务器端控件,经过一番思量后确定前端采用原始的html.后台采用Linq to ...
- vue、react、angular三大框架对比 && 与jQuery的对比
前端当前最火的三大框架当属vue.react以及angular了. 但是在做项目的时候,我们怎么去选择呢? 这里做一个比较,希望大家可以有一个比较清晰的认识. vue与react vue和react ...
- Jquery—Jquery异步功能实例
Jquery确实是一个非常好的JavaScript框架,今天利用闲暇时间给大家一个借助Jquery异步实现校验username的唯一性的样例: 代码1--index.jsp文件: <%@ pag ...
随机推荐
- codeforces_1075_C. The Tower is Going Home
http://codeforces.com/contest/1075/problem/C 题意:一个长宽均为1e9的棋盘,n个垂直障碍在x列无限长,m个水平障碍在第y行从第x1列到x2列.可以水平和垂 ...
- Android(java)学习笔记189:ContentProvider使用(银行数据库创建和增删改查的案例)
1. Android的四大组件: (1)Activity 用户交互的UI界面 (2)Service 后台运行的服务 (3)BroadcastReceiver 广播接收者 (4)ContentPro ...
- redis-3.0.1 sentinel 主从高可用 详细配置
最近项目上线部署,要求redis作高可用,由于redis cluster还不是特别成熟,就选择了redis sentinel做高可用.redis本身有replication,实现主从备份.结合sent ...
- MFC_简易文件管理器
练习_简易文件管理器 Edit1编辑框绑定变量,初始化内容 m_EditCtrl = L"D:\"; 添加List控件,属性设置report,OnInitDialog()函数里添加 ...
- 比较 String,StringBuffer,StringBuilder
1)三者在执行速度方面的比较:StringBuilder > StringBuffer > String 2)String <(StringBuffer,StringBuild ...
- cesium 原理 之 command拼接
VAO VAO(Vertext Array Object),中文是顶点数组对象.之前在<Buffer>一文中,我们介绍了Cesium如何创建VBO的过程,而VAO可以简单的认为是基于VBO ...
- 使用soapUI5.3.0调试webservice接口(参数为XML格式)
最近项目中经常要调试webservice接口,从朋友处了解到他们经常使用SoapUI,因此学习一下这个工具的使用,为避免遗忘,特地记录下来,分享分享... 下载 #####首先,下载SoapUI,我下 ...
- 微信小程序------微信支付模块
最近项目涉及到小程序开发:需要进行微信支付模块,接下来通过叙述,记录一下微信小程序中微信支付模块的开发,以便日后翻阅和使用. 学习指南----------微信支付开发文档:https://pay.we ...
- Android反编译初步
网上关于Android反编译的帖子很多,反编译的步骤也是很详细,本文Android反编译参考博客:https://www.cnblogs.com/dhcn/p/7120891.html 而反编译中最主 ...
- C#线程锁使用全功略
C#线程锁使用全功略 前两篇简单介绍了线程同步lock,Monitor,同步事件EventWaitHandler,互斥体Mutex的基本用法,在此基础上,我们对 它们用法进行比较,并给出什么时候需要锁 ...