Jquery中的异步编程浅析 延期(deferred)的承诺(promise)

引子

相信各位developers对js中的异步概念不会陌生,异步操作后的逻辑由回调函数来执行,回调函数(callback function)顾名思义就是“回头调用的函数”,函数体事先已定义好,在未来的某个时候由某个事件触发调用,而这个时机,是程序本身无法控制的。

举几个常见例子:

  • 事件绑定

  • 动画

  • Ajax

上面的例子简单、典型,易于阅读和理解。

为了引出本文的主题,假设现在有3个ajax异步操作,分别为A、B、C,每个都封装成了函数,并可传入success回调作为参数。

请考虑以下场景:

  1. 希望这3个异步操作按顺序执行,形成执行队列,即A执行完了,B执行;B执行完了,C执行;
  2. 希望A,B完成了,再执行C;

对于场景(1),代码大概会是这样:

按传统的写法,自然而然会形成回调函数的多层嵌套,如果代码量比较多的话,代码的可读性和维护性会比较差。

对于场景(2),代码可能要复杂些了:

  • a) A、B操作分别需要有状态变量isADoneisBDone,默认值均为false;A成功后isADone置为true,B成功后isBDone置为true
  • b) 需要一段代码来反复探测上述2个状态变量,我们把这个行为称为pending,等pending到2个状态都为true时则执行C,并终止pending;

就本场景而言,上述方案还可勉强接受,如果情况再复杂些,代码的可读性和维护性也会大打折扣。

以上2种场景,只限定了3个异步操作的执行关系,实际的开发场景可能要比这个复杂的多,为了开发者能够更优雅的处理js异步操作,jq引入了Deferred对象($.Deferred),我们先来了解下Deferred对象,然后看看它能为我们的异步编程带来哪些益处。

Deferred特性介绍

先创建一个Deferred实例。

new关键字是可选的,可以不写,这是因为在Deferred构造函数中处理了,但不代表用原生的js基于构造函数创建对象不用写new

deferred带有3种状态:pending(待定)、resolved(成功)、rejected(失败)。

deferred的状态可以通过api进行切换,但不可逆。

可从英文字面意思理解:resolve(解决)后成功,reject(拒绝)后失败。

状态不可逆,是指一旦从待定状态切换到任何一个确定状态后,再次调用resolvereject对原状态将不起任何作用。

deferred通过语义对应的api来绑定不同状态时执行的回调函数。

resolve: done, always

reject: fail, always

其中always绑定的回调函数,不论deferred的状态是成功或失败,总会执行。

deferred还有一个then方法,来简化donefail的写法:

另外,值得注意的是,当deferred状态切换后(调用reject或者resolve后),再进行回调函数的绑定,那么对应状态的回调函数会立即执行。

举个例子:

deferred还可以返回自己的操作子集。

promise只有绑定回调的api,而没有状态切换的api。

deferred: reject, resolve, done, fail, then, always

promise: done, fail, then, always

很明显promise是用来开放给外部调用的,而deferred通常用于模块内部,来控制自身状态的切换。

举个直观的例子,一般在js中我们会用setTimeout来做延时执行,如:

我们用deferred来封装下,代码如下:

wait函数内部,通过deferred来进行状态切换,返回promise对象,这样就可以在wait外部进行回调函数的绑定。

其实该原理在jq中有个非常典型的案例,那就是$.ajax方法,该方法会返回一个promise,而方法内部有个deferred用于决定自身状态,所以相对传统的ajax写法,我们也可以这么写:

其中donefail等方法还可以接受多个callback、或者以callback array作为参数。

假定有个ajax请求,成功后需要对返回结果进行3个独立的处理,分别对应3个函数,我们可以这么写:

这样获取数据的逻辑和处理数据的逻辑进行了分离,数据处理不会全都堆在success回调函数中,代码整体看起来就更简洁易读。

回到之前的问题

再了解了Deferred特性和简单应用后,我们再回头考虑前面提到的2个场景,是否能够用更好的方案来实现呢?答案是肯定的。

我们先看场景(2):A、B完成了,再执行C。

为什么先看场景(2),因为jq中正好有个现成的api:$.when,能够很方便的满足该需求。

$.when可接受多个deferred(promise)对象,$.when会返回一个promise,用作后续回调的绑定,官网示例如下:

$.when中2个异步请求均返回成功后,即会执行myFunc回调;

$.when中只要有一个异步请求失败,即会执行myFailure回调;

解决方案已经很清晰了,我们稍加改造之前的asyncAasyncB方法,让它们分别返回各自的promise,然后直接使用$.when即可。

我可以先用ajax来做代码示例:

下面再来个直接使用deferred的代码实例:

整体上,是否感觉代码更清晰,更利于理解了呢?

我们再来看场景(1):A执行完了,B执行;B执行完了,C执行。

要完成这个实现的改造,需要深入了解下deferred对象的then方法。上面我们介绍了then的一般用法:

用于donefail的简化写法。但它还有一个非常重要的作用,可以用来传递deferred(promise)对象,实现任务链条(chain tasks)。

下面就用then来实现场景(1)的需求:

是不是感觉很简单,如果C后面还有D,那继续往下then就可以了。

假定asyncA是一个ajax操作,其中回调函数data参数,即为ajax成功后,后台返回的数据。

值得注意的是,如果用then方法进行deferred对象的传递,回调函数必须return一个deferred

上述的例子还有一个特点:由于then的第一个参数是deferred对象成功时执行的回调,若deferred状态切换到失败,则后续then的成功回调将不再执行,任务链就中断了。

该场景有一定的实践价值,比如一个业务网站,页面上有多个展示模块都是通过ajax问后台拿数据的,如果页面一进来,同时向后台发好几个ajax请求,会瞬间增加后台IO的压力,可能会增加用户等待界面反馈数据的时间,造成体验下降。在这种情况下,把ajax请求作为队列处理是比较合适的,可按重要性逐步请求获取数据,提高性能和渲染体验。

小结

本文只是初步的介绍了下jq中的Deferred,并没有深入到每一个细节,但它的基本功用应该是覆盖到了,感兴趣的同学可以前往jq官网,自行研究下。

其实promise就可以按照字面理解为“承诺”,可以把deferred理解成你的一个手下,你让他去跟进一件事,而这个事情什么时候可以有个结果,你并不清楚,但他会给你承诺,将按照你的意图:事情若这样,后续要做什么;事情若那样,后续要做什么

时间: 2024-12-25 23:00:13

Jquery中的异步编程浅析 延期(deferred)的承诺(promise)的相关文章

Async in C# 5.0(C#中的异步编程Async) 蜗牛翻译之第一章

p { display: block; margin: 3px 0 0 0; } --> 写在前面 在学异步,有位园友推荐了<async in C#5.0>,没找到中文版,恰巧也想提高下英文,用我拙劣的英文翻译一些重要的部分,纯属娱乐,简单分享,保持学习,谨记谦虚. 如果你觉得这件事儿没意义翻译的又差,尽情的踩吧.如果你觉得值得鼓励,感谢留下你的赞,祝各位爱技术的园友在今后每一次应该猛烈突破的时候,不选择知难而退.在每一次应该独立思考的时候,不选择随波逐流,应该全力以赴的时候,不选择尽力

.Net中的异步编程

.Net中的异步编程? .net中实现异步有两种方式:第一种是多线程的方式,第二种是使用异步函数,其实在异步函数中使用的还是多线程的技术. 异步编程中比较关注也比较重要的技术点在于:1.当异步线程在工作完成时如何通知调用线程:2.当异步线程出现异常的时候该如何处理: 3.异步线程工作的进度如何实时的通知调用线程:4.如何在调用线程中取消正在工作的异步线程,并进行回滚操作. 虽然在.net中提供了众多的异步编程模式,但是推荐最好使用Task类,因为Task类使用线程池中的任务线程,又由线程池管理,

JavaScript强化教程——JQuery中的DOM编程

本文为 H5EDU 机构官方 HTML5培训 教程,主要介绍:JavaScript强化教程 --JQuery中的DOM编程 JavaScript强化教程--JQuery中的DOM编程 本文为 H5EDU 机构官方 HTML5培训 教程,主要介绍:JavaScript强化教程 --JQuery中的DOM编程 DOM编程 ---------------------------- js中DOM编程   创建一个标签对象     var p=document.createElement("p"

C#中的异步编程Async 和 Await

谈到C#中的异步编程,离不开Async和Await关键字 谈到异步编程,首先我们就要明白到底什么是异步编程. 平时我们的编程一般都是同步编程,所谓同步编程的意思,和我们平时说的同时做几件事情完全不同. 在计算机的世界里,同步编程的意思说 按照顺序来执行,或者说是 一个接着一个地有序的来执行, 比如目前我们在代码中有三件任务来执行,那么必须先执行完第1件,再执行第2件,接下来再执行第3件. 在这个过程中,第1件没有完成,你是没法开始做第2件事情的,必须等待. 比如一个人烧开水需要10分钟,5分钟找

延期(deferred)的承诺(promise) —— jq异步编程浅析

引子 相信各位developers对js中的异步概念不会陌生,异步操作后的逻辑由回调函数来执行,回调函数(callback function)顾名思义就是“回头调用的函数”,函数体事先已定义好,在未来的某个时候由某个事件触发调用,而这个时机,是程序本身无法控制的. 举几个常见例子: 事件绑定 动画 Ajax 上面的例子简单.典型,易于阅读和理解. 为了引出本文的主题,假设现在有3个ajax异步操作,分别为A.B.C,每个都封装成了函数,并可传入success回调作为参数. 请考虑以下场景: 希望

在JavaScript中实现异步编程模式的方法

本文总结了”异步模式”编程的4种方法,理解它们可以让你写出结构更合理.性能更出色.维护更方便的Javascript程序. 一.回调函数 这是异步编程最基本的方法. 假定有两个函数f1和f2,后者等待前者的执行结果. f1(); f2(); 如果f1是一个很耗时的任务,可以考虑改写f1,把f2写成f1的回调函数. function f1(callback){ setTimeout(function () { // f1的任务代码 callback(); }, 1000); } 执行代码就变成下面这

全面解析C#中的异步编程

当我们处理一些长线的调用时,经常会导致界面停止响应或者IIS线程占用过多等问题,这个时候我们需要更多的是用异步编程来修正这些问题,但是通常都是说起来容易做起来难,诚然异步编程相对于同步编程来说,它是一种完全不同的编程思想,对于习惯了同步编程的开发者来说,在开发过程中难度更大,可控性不强是它的特点. 在.NET Framework5.0种,微软为我们系统了新的语言特性,让我们使用异步编程就像使用同步编程一样相近和简单,本文中将会解释以前版本的Framework中基于回调道德异步编程模型的一些限制以

promise 的基本概念 和如何解决js中的异步编程问题 对 promis 的 then all ctch 的分析 和 await async 的理解

* promise承诺 * 解决js中异步编程的问题 * * 异步-同步 * 阻塞-无阻塞 * * 同步和异步的区别? 异步;同步 指的是被请求者 解析:被请求者(该事情的处理者)在处理完事情的时候的通知机制. 异步:当事情处理完成后被请求者会发信息通知请求者该事情处理完成.在这期间被请求者可以选择是继续等待命令请求完成还是去做其他事等待被请求者返回. 同步:当事情处理完成后被请求者不会告知请求者,等到请求者发来询问是才会告知 阻塞:非阻塞 指的是请求者 阻塞:针对请求者来说的,委托其他人处理一

.NET中的异步编程——动机和单元测试

背景 自.NET 4.5发布以来已经有很长一段时间了.留在了我们的记忆里,其发布在2012年8月15日.是的,六年前.感觉老了吗?好吧,我不打算让你做出改变,而是提醒你一些.NET发布的亮点.此版本带来的主要功能之一是使用async / await方法进行异步编程.基本上,微软的团队通过保持类似于同步代码的逻辑结构,使编译器完成开发人员过去经常做的工作. 你看,在那个时候,Windows Phone仍然是一件事情,为这些平台开发应用程序有一定的局限性.主要原因是Windows Phone与桌面应