热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

jQuery源码学习(五)异步机制

异步机制javascript编程往往会伴随着异步编程,比如远程获取数据,大量的异步编程会带来许多的回调函数,js是单线程的,所以完成异步往

异步机制

Javascript编程往往会伴随着异步编程,比如远程获取数据,大量的异步编程会带来许多的回调函数,js是单线程的,所以完成异步往往需要借助浏览器事件驱动,这样会让我们的代码和算法变得支离破碎。jQuery就提供了一个抽象的 非阻塞 解决方案: Deferred

认识异步

alert(1)
setTimeout(function(){alert(2)
},0)
alert(3)
//alert(1)
//alert(3)
//alert(2)

一、认识 deferred对象

开发网站的过程中,我们经常遇到某些耗时很长的Javascript操作。其中,既有异步的操作(比如ajax读取服务器数据),也有同步的操作(比如遍历一个大型数组),它们都不是立即能得到结果的。

通常的做法是,为它们指定回调函数(callback)。即事先规定,一旦它们运行结束,应该调用哪些函数。

deferred对象是在jquery 1.5版本被引进来的,jquery在回调方面的出处理不是很好,为此就诞生了deferred对象了。deferred对象就是jQuery的回调函数解决方案。在英语中,defer的意思是"延迟",所以deferred对象的含义就是"延迟"到未来某个点再执行。

二、ajax的链式操作

最开始写法是:

// <1.5
$.ajax({url: "test.html",success: function(){alert("哈哈&#xff0c;成功了&#xff01;");},error:function(){alert("出错啦&#xff01;");}});

如果jquery的版本是1.5以下的&#xff0c;那么返回的是XHR对象&#xff0c;所以没法链式操作&#xff0c;但是高于1.5的版本的话&#xff0c;返回的是Deferred对象,可以进行链式操作。

// >&#61;1.5
$.ajax("test.html").done(function(){}).fail(function(){})

done()相当于success方法&#xff0c;fail()相当于error方法。采用链式写法以后&#xff0c;代码的可读性大大提高。

三、指定同一操作的多个回调函数

deferred对象的一大好处&#xff0c;就是它允许你自由添加多个回调函数。

还是以上面的代码为例&#xff0c;如果ajax操作成功后&#xff0c;除了原来的回调函数&#xff0c;我还想再运行一个回调函数&#xff0c;怎么办&#xff1f;

$.ajax("test.html").done(function(){ alert("哈哈&#xff0c;成功了&#xff01;");} ).fail(function(){ alert("出错啦&#xff01;"); } ).done(function(){ alert("第二个回调函数&#xff01;");} );

四、为多个操作指定回调函数

deferred对象的另一大好处&#xff0c;就是它允许你为多个事件指定一个回调函数&#xff0c;这是传统写法做不到的。那就是$.when()

$.when($.ajax("test1.html"), $.ajax("test2.html")).done(function(){ alert("哈哈&#xff0c;成功了&#xff01;"); }).fail(function(){ alert("出错啦&#xff01;"); });
//$.ajax("test1.html")和$.ajax("test2.html")&#xff0c;如果都成功了&#xff0c;就运行done()指定的回调函数&#xff1b;如果有一个失败或都失败了&#xff0c;就执行fail()指定的回调函数。

五、普通操作的回调函数接口扩展

它把这一套回调函数接口&#xff0c;从ajax操作扩展到了所有操作。也就是说&#xff0c;任何一个操作----不管是ajax操作还是本地操作&#xff0c;也不管是异步操作还是同步操作----都可以使用deferred对象的各种方法&#xff0c;指定回调函数。

// 一个耗时操作
var wait &#61; function(){var task &#61; function(){alert(&#39;执行了&#39;);};setTimeout(5000);
}

我们为其添加回调,可能会想到$.when();

$.when(wait()).done(function(){ alert("哈哈&#xff0c;成功了&#xff01;"); }).fail(function(){ alert("出错啦&#xff01;"); });

但是没起到效果&#xff0c;done立即执行&#xff0c;原因是$.when()接受的是一个deferred对象。所以我们进一步改进吧

var dtd &#61; $.Deferred();
var wait &#61; function(){var task &#61; function(){alert(&#39;执行了&#39;);dtd.resolve(); // 改变deferred对象的执行状态};setTimeout(task, 5000);return dd;
}

这样wait()返回的是一个defferred对象了&#xff0c;这就可以上链式操作了。

$.when(wait()).done(function(){ alert("哈哈&#xff0c;成功了&#xff01;"); }).fail(function(){ alert("出错啦&#xff01;"); });

六、defferred.resolve() 和 defferred.reject()

deferred采用的是Promise的原理。这里我们要清楚一个执行状态的概念。在defferred对象中&#xff0c;有着三种状态&#xff0c;未完成 已完成 已失败

如果执行状态是"已完成"&#xff08;resolved&#xff09;,deferred对象立刻调用done()方法指定的回调函数&#xff1b;
如果执行状态是"已失败"&#xff0c;调用fail()方法指定的回调函数&#xff1b;
如果执行状态是"未完成"&#xff0c;则继续等待&#xff0c;或者调用progress()方法指定的回调函数&#xff08;jQuery1.7版本添加&#xff09;。

defferred.resolve()是将状态从未完成变为已完成。 &#61;&#61;> done()
defferred.reject()是将状态从未完成变为了已失败。 &#61;&#61;> fail()

var dtd &#61; $.Deferred(); // 新建一个Deferred对象var wait &#61; function(){var tasks &#61; function(){alert("执行完毕&#xff01;");dtd.resolve(); // 改变Deferred对象的执行状态};setTimeout(tasks,5000);return dtd; // 返回promise对象};$.when(wait()).done(function(){ alert("哈哈&#xff0c;成功了&#xff01;"); }).fail(function(){ alert("出错啦&#xff01;"); });dtd.resolve();// 这里会立即先执行一次

七、defferred.promise();

上面的方法是存在一些问题的&#xff0c;比如dtd是一个全局变量&#xff0c;他可以在外部被随时该边状态&#xff0c;这样就会使得done()或者fail()执行不只有一次。

这显然不是我们想要的&#xff0c;所以我们就需要defferred.promise的帮忙了。

它的作用是&#xff0c;在原来的deferred对象上返回另一个deferred对象&#xff0c;后者只开放与改变执行状态无关的方法&#xff08;比如done()方法和fail()方法&#xff09;&#xff0c;屏蔽与改变执行状态有关的方法&#xff08;比如resolve()方法和reject()方法&#xff09;&#xff0c;从而使得执行状态不能被改变。

var wait &#61; function(dtd){var dtd &#61; $.Deferred(); //在函数内部&#xff0c;新建一个Deferred对象var tasks &#61; function(){alert("执行完毕&#xff01;");dtd.resolve(); // 改变Deferred对象的执行状态};setTimeout(tasks,5000);return dtd.promise(); // 返回promise对象};var dd &#61; wait();// dd.resolve(); // 这里如果使用了的话&#xff0c;就会报错的哦&#xff0c;因为返回deferred.promise&#xff08;&#xff09;是无法对状态修改的哦$.when(dd).done(function(){ alert("哈哈&#xff0c;成功了&#xff01;"); }).fail(function(){ alert("出错啦&#xff01;"); });

八、$.Defferred(wait)

另一种防止执行状态改变的方法是使用$.Defferred()


var dtd &#61; $.Deferred(); // 新建一个Deferred对象var wait &#61; function(dtd){var tasks &#61; function(){alert("执行完毕&#xff01;");dtd.resolve(); // 改变Deferred对象的执行状态};setTimeout(tasks,5000);return dtd;};
var ddd &#61; wait();
// dd.resolve(); //这里报错了哦
$.Deferred(dd).done(function(){ alert("哈哈&#xff0c;成功了&#xff01;"); }).fail(function(){ alert("出错啦&#xff01;"); });

九、defferred对象小结

  • $.Deferred() 生成一个deferred对象。
  • deferred.done() 指定操作成功时的回调函数
  • deferred.fail() 指定操作失败时的回调函数
  • deferred.promise() 没有参数时&#xff0c;返回一个新的deferred对象&#xff0c;该对象的运行状态无法被改变&#xff1b;接受参数时&#xff0c;作用为在参数对象上部署deferred接口。
  • deferred.resolve(args) 手动改变deferred对象的运行状态为"已完成"&#xff0c;从而立即触发done()方法。解决Deferred&#xff08;延迟&#xff09;对象&#xff0c;并根据给定的args参数调用任何完成回调函数&#xff08;doneCallbacks&#xff09;。
  • deferred.resolveWith() 解决Deferred&#xff08;延迟&#xff09;对象&#xff0c;并根据给定的 context和args参数调用任何完成回调函数&#xff08;doneCallbacks&#xff09;。
  • deferred.reject(args) 这个方法与deferred.resolve()正好相反&#xff0c;调用后将deferred对象的运行状态变为"已失败"&#xff0c;从而立即触发fail()方法。拒绝Deferred&#xff08;延迟&#xff09;对象&#xff0c;并根据给定的args参数调用任何失败回调函数&#xff08;failCallbacks&#xff09;。这里的args是一个对象。
  • deferred.rejectWith(context, args) 拒绝Deferred&#xff08;延迟&#xff09;对象&#xff0c;并根据给定的 context和args参数调用任何失败回调函数&#xff08;failCallbacks&#xff09;。这里的args是一个数组类型。
  • $.when() 为多个操作指定回调函数。除了这些方法以外&#xff0c;deferred对象还有二个重要方法&#xff0c;上面的教程中没有涉及到。Context&#xff08;上下文&#xff09; 作为 this对象传递给失败回调函数&#xff08;failCallbacks &#xff09;
  • deferred.then()

有时为了省事&#xff0c;可以把done()和fail()合在一起写&#xff0c;这就是then()方法。

$.when($.ajax( "/main.php" )).then(successFunc, failureFunc );

如果then()有两个参数&#xff0c;那么第一个参数是done()方法的回调函数&#xff0c;第二个参数是fail()方法的回调方法。如果then()只有一个参数&#xff0c;那么等同于done()。

  • deferred.always() 这个方法也是用来指定回调函数的&#xff0c;它的作用是&#xff0c;不管调用的 deferred.resolve()还&#96;是deferred.reject()&#xff0c;最后总是执行。

$.ajax( "test.html" ).always( function() { alert("已执行&#xff01;");} );

  • deferred.state() 确定一个Deferred&#xff08;延迟&#xff09;对象的当前状态。
  1. "pending": Deferred对象是尚未完成状态 (不是 "rejected" 或 "resolved").
  2. "resolved": Deferred对象是在解决状态&#xff0c;这意味着&#xff0c;deferred.resolve() 或者 deferred.resolveWith()被对象访问和doneCallbacks被访问&#xff08;或在被调用的过程中&#xff09;
  3. "rejected": Deferred对象是在被拒绝的状态&#xff0c;这意味着&#xff0c;deferred.reject() 或者 deferred.rejectWith() 被对象访问和failCallbacks被访问&#xff08;或在被调用的过程中&#xff09; 。

这种方法主要是用在调试&#xff0c;例如&#xff0c;在准备拒绝&#xff08;reject&#xff09;一个延迟对象前&#xff0c;判断它是否已经处于 resolved 状态。


十、使用实例

情景1&#xff1a;当用户按下删除弹窗的确定或取消后&#xff0c;把弹窗隐藏&#xff0c;并执行对应的操作&#xff08;删除或不执行&#xff09;&#xff0c;因为我们不知道用户什么时候会点击按钮&#xff0c;所以不能让弹窗阻塞其他任务的执行。

function pop(arg) {if (!arg) {console.error(&#39;pop title is empty&#39;);}var dfd &#61; $.Deferred() //实例化一个延迟对象 ,confirmed //记录按下确定或取消 , $confirm &#61; $content.find(&#39;button.confirm&#39;) //确认按钮 ,$cancel &#61; $content.find(&#39;button.cancel&#39;); //取消按钮 //定时器轮询&#xff0c;当按下确定或取消时触发删除或取消操作 timer &#61; setInterval(function() {if (confirmed !&#61;&#61; undifined) {dfd.resolve(confirmed);clearInterval(timer);dismiss_pop();}}, 50);//点击确定时更改confirmed状态 $confirm.on(&#39;click&#39;, function() {confirmed &#61; true;});//点击取消时更改confirmed状态 $cancel.on(&#39;click&#39;, function() {confirmed &#61; false;}); //返回dfd对象return dfd.promise();}$(&#39;.delete&#39;).click(function() {var $this &#61; $(this);var index &#61; $this.data(&#39;index&#39;);//当前的id //确定删除pop(&#39;确定删除&#xff1f;&#39;).then(function(res) {res ? delete_task(index) : null;})})

参考文献

阮一峰的defferred理解

allenm



推荐阅读
  • Voicewo在线语音识别转换jQuery插件的特点和示例
    本文介绍了一款名为Voicewo的在线语音识别转换jQuery插件,该插件具有快速、架构、风格、扩展和兼容等特点,适合在互联网应用中使用。同时还提供了一个快速示例供开发人员参考。 ... [详细]
  • CSS3选择器的使用方法详解,提高Web开发效率和精准度
    本文详细介绍了CSS3新增的选择器方法,包括属性选择器的使用。通过CSS3选择器,可以提高Web开发的效率和精准度,使得查找元素更加方便和快捷。同时,本文还对属性选择器的各种用法进行了详细解释,并给出了相应的代码示例。通过学习本文,读者可以更好地掌握CSS3选择器的使用方法,提升自己的Web开发能力。 ... [详细]
  • Ihavethefollowingonhtml我在html上有以下内容<html><head><scriptsrc..3003_Tes ... [详细]
  • 本文介绍了Java后台Jsonp处理方法及其应用场景。首先解释了Jsonp是一个非官方的协议,它允许在服务器端通过Script tags返回至客户端,并通过javascript callback的形式实现跨域访问。然后介绍了JSON系统开发方法,它是一种面向数据结构的分析和设计方法,以活动为中心,将一连串的活动顺序组合成一个完整的工作进程。接着给出了一个客户端示例代码,使用了jQuery的ajax方法请求一个Jsonp数据。 ... [详细]
  • 本文介绍了在开发Android新闻App时,搭建本地服务器的步骤。通过使用XAMPP软件,可以一键式搭建起开发环境,包括Apache、MySQL、PHP、PERL。在本地服务器上新建数据库和表,并设置相应的属性。最后,给出了创建new表的SQL语句。这个教程适合初学者参考。 ... [详细]
  • Html5-Canvas实现简易的抽奖转盘效果
    本文介绍了如何使用Html5和Canvas标签来实现简易的抽奖转盘效果,同时使用了jQueryRotate.js旋转插件。文章中给出了主要的html和css代码,并展示了实现的基本效果。 ... [详细]
  • 本文介绍了前端人员必须知道的三个问题,即前端都做哪些事、前端都需要哪些技术,以及前端的发展阶段。初级阶段包括HTML、CSS、JavaScript和jQuery的基础知识。进阶阶段涵盖了面向对象编程、响应式设计、Ajax、HTML5等新兴技术。高级阶段包括架构基础、模块化开发、预编译和前沿规范等内容。此外,还介绍了一些后端服务,如Node.js。 ... [详细]
  • 本文介绍了如何使用jQuery和AJAX来实现动态更新两个div的方法。通过调用PHP文件并返回JSON字符串,可以将不同的文本分别插入到两个div中,从而实现页面的动态更新。 ... [详细]
  • 从零基础到精通的前台学习路线
    随着互联网的发展,前台开发工程师成为市场上非常抢手的人才。本文介绍了从零基础到精通前台开发的学习路线,包括学习HTML、CSS、JavaScript等基础知识和常用工具的使用。通过循序渐进的学习,可以掌握前台开发的基本技能,并有能力找到一份月薪8000以上的工作。 ... [详细]
  • Jquery 跨域问题
    为什么80%的码农都做不了架构师?JQuery1.2后getJSON方法支持跨域读取json数据,原理是利用一个叫做jsonp的概念。当然 ... [详细]
  • 本文介绍了DataTables插件的官方网站以及其基本特点和使用方法,包括分页处理、数据过滤、数据排序、数据类型检测、列宽度自动适应、CSS定制样式、隐藏列等功能。同时还介绍了其易用性、可扩展性和灵活性,以及国际化和动态创建表格的功能。此外,还提供了参数初始化和延迟加载的示例代码。 ... [详细]
  • 本文总结了在编写JS代码时,不同浏览器间的兼容性差异,并提供了相应的解决方法。其中包括阻止默认事件的代码示例和猎取兄弟节点的函数。这些方法可以帮助开发者在不同浏览器上实现一致的功能。 ... [详细]
  • android listview OnItemClickListener失效原因
    最近在做listview时发现OnItemClickListener失效的问题,经过查找发现是因为button的原因。不仅listitem中存在button会影响OnItemClickListener事件的失效,还会导致单击后listview每个item的背景改变,使得item中的所有有关焦点的事件都失效。本文给出了一个范例来说明这种情况,并提供了解决方法。 ... [详细]
  • 本文介绍了九度OnlineJudge中的1002题目“Grading”的解决方法。该题目要求设计一个公平的评分过程,将每个考题分配给3个独立的专家,如果他们的评分不一致,则需要请一位裁判做出最终决定。文章详细描述了评分规则,并给出了解决该问题的程序。 ... [详细]
  • Python如何调用类里面的方法
    本文介绍了在Python中调用同一个类中的方法需要加上self参数,并且规范写法要求每个函数的第一个参数都为self。同时还介绍了如何调用另一个类中的方法。详细内容请阅读剩余部分。 ... [详细]
author-avatar
gql199111
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有