通过 ES6 Promise 和 jQuery Deferred 的异同学习 Promise
Deferred 和 Promise
ES6 和 jQuery 都有 Deffered 和 Promise,但是略有不同。不过它们的作用可以简单的用两句话来描述
- Deffered 触发 resolve 或 reject
- Promise 中申明 resolve 或 reject 后应该做什么(回调)
在 jQuery 中
var deferred = $.Deferred(); var promise = deferred.promise();
在 ES6 中
var deferred = Promise.defer(); var promise= defered.promise;
MDN 宣布 Deferred 在 Gecko 30 中被申明为过期,不应该再使用,而应该用
new Promise()
来代替。关于new Promise()
将在后面说明。
jQuery 的 Deferred/Promise
jQuery 中最常用的 Promise 对象是 $.ajax()
返回的,最常用的方法不是 then
,而是 done
、fail
和 always
。除了 $.ajax()
外,jQuery 也提供了 $.get()
、$.post()
和 $.getJSON()
等简化 Ajax 调用,它们返回的和 $.ajax()
的返回值一样,是个 Promise 对象。
实际上
$.ajax()
返回的是一个 jqXHR 对象。但 jqXHR 实现了 jQuery 的 Promise 接口,所以也是一个 Promise 对象。
done()
、fail()
和 always()
done()
添加 deferred.resolve()
的回调,fail()
添加 deferred.reject()
的回调。所以在 Ajax 调用成功的情况下执行 done()
添加的回调,调用失败时执行 fail()
添加的回调。但不管成功与否,都会执行 always()
添加的回调。
这里 done()
、fail()
和 always()
都是以类似事件的方式添加回调,也就意味着,不管执行多次次 done()
、fail()
或 always()
,它们添加的若干回调都会在符合的条件下依次执行。
一般情况下会这样执行 Ajax
// 禁用按钮以避免重复提交 $("#theButton").prop({ disabled: true }); // 调用 Ajax 提交数据,假设返回的是 JSON 数据 var jqxhr = $.ajax("do/example", { type: "post", dataType: "json", data: getFormData() }); jqxhr.done(function(jsonObject) { // Ajax 调用成功 console.log("success with data", jsonObject); }).fail(function() { // Ajax 调用失败 console.log("failed") }).always(function() { // 不管成功与否,都会执行,取消按钮的禁用状态 $("#theButton").prop({ disabled: false }); });
上面是最普通最常用的用法,但是在一个项目中总是这么写 Ajax,有点累,稍微约定一下再封装一下就使用起来就会便捷得多。首先,假设我们定义返回的 JSON 是这样的格式:
{ "code": "int, 0 表示成功,其它值表示出错", "message": "string, 附加的消息,可选", "data": "object,附加的数据,可选 }
然后为项目公共类 app
定义一个 ajax
方法
app.ajax = function(button, url, data) { if (button) { button.prop("disabled", true); } return $.ajax(url, { type: "post", dataType: "json", data: data }).done(function(json) [ if (json.code !== 0) { showError(json.message || "操作发生错误"); } }).fail(function() { showError("服务器错误,请稍后再试"); }).always(function() { if (button) { button.prop("disabled", false); } }); }; // 调用 app.ajax("do/example", getFormData()).done(function(json) { if (json.code === 0) { // 只需要处理正确的情况啦 } });
不过还是有点不爽,如果不需要判断 json.code === 0
就更好了。这个……可以自己用一个 Deferred 来处理:
app.ajax = function(button, url, data) { if (button) { button.prop("disabled", true); } var deferred = $.Deferred(); $.ajax(url, { type: "post", dataType: "json", data: data }).done(function(json) [ if (json.code !== 0) { showError(json.message || "操作发生错误"); deferred.reject(); } else { deferred.resolve(json); } }).fail(function() { showError("服务器错误,请稍后再试"); deferred.reject(); }).always(function() { if (button) { button.prop("disabled", false); } }); return deferred.promise(); }; // 调用 app.ajax("do/example", getFormData()).done(function(json) { // json.code === 0 总是成立 // 正常处理 json.data 就好 });
注意,这里已经不是直接返回 $.ajax()
的结果 jqXHR 对象了,返回的是新建 Deferred
对象的 promise
对象。
复习了 Ajax,现在需要切入正题,找到 jQuery Promise 和 ES6 Promise 接近的地方——then()
。
jQuery deferred.then()
在 jQuery 1.8 以前(不含 1.8,比如 jQuery 1.7.2),deferred.then()
就是一个把 done()
和 fail()
放在一起的语法糖。jQuery 在 1.8 版本的时候修改了 deferred.then()
的行为,使 then()
的行为与 Promise 的 then()
相似。从 jQuery 的文档可以看到 1.8 版本的变化——干掉了 callback,换成了 filter:
// version added: 1.5, removed: 1.8 deferred.then( doneCallbacks, failCallbacks ) // version added: 1.7, removed: 1.8 deferred.then( doneCallbacks, failCallbacks [, progressCallbacks ] ) // version added: 1.8 deferred.then( doneFilter [, failFilter ] [, progressFilter ] )
可以简单的把 callback 当作一个事件处理,值用于 callback 之后一般不会改变;而 filter 不同,一个值传入 filter 再从 filter 返回出来,可能已经变了。还是举个例子来说明
var deferred = $.Deferred(); var promise = deferred.promise(); promise.then(function(v) { console.log(`then with ${v}`); }).done(function(v) { console.log(`done with ${v}`); }); deferred.resolve("resolveData");
在 jQuery 1.7.2 中的结果
then with resolveData done with resolveData
在 jQuery 1.8.0 中的结果
then with resolveData done with undefined
从上面来看,jQuery 的 deferred.then()
语义和 ES6 Promise.then()
语义基本一致。如果把上面的 app.ajax
换成 then()
实现会有助于对 ES6 Promise 的理解。
app.ajax = function(button, url, data) { if (button) { button.prop("disabled", true); } return $.ajax(url, { type: "post", dataType: "json", data: data }).then(function(json) { if (json.code !== 0) { showError(json.message || "操作发生错误"); return $.Deferred().reject().promise(); } else { return $.Deferred().resolve(json).promise(); } }, function() { showError("服务器错误,请稍后再试"); deferred.reject(); }).always(function() { if (button) { button.prop("disabled", false); } }); }; // 调用方式没变,用 done,也可以用 then app.ajax("do/example", getFormData()).done(function(json) { // json.code === 0 总是成立 // 正常处理 json.data 就好 });
从 jQuery Promise 到 ES6 Promise
上面的代码太长,提炼一下关键部分(示意,不能运行)
var promise = $.ajax(); promise.then(function(data) { // resolve return data.code ? new Promise().reject() : new Promise().resolve(data); // 如果没有错,就返回一个新的 promise,并使用 data 来 resolve, // 也可以直接返回 data, // 这样后面 then 的 resolve 部分才能收到数据 }, function() { // rejected }); // 调用阶段 promise.then(function(data) { // 处理 data });
也许你没注意到,其实上面的代码基本上就是 ES6 的 Promise 了。下面正式用 ES6 Promise 改写上面的示意代码
var promise = new Promise(function(resolve, reject) { $.ajax().then(resolve, reject); // 上面这句没看懂?那换成这样你一定会懂 // $.ajax().then(function(data) { // resolve(data); // }, function() { // reject(); // }); }).then(function(data) { return data.code ? Promise.reject() : Promise.resolve(data); // 这里 Promise.resolve(data) 同样可以直接替换为 data }); // 调用没变 promise.then(function(data) { // 处理 data });
怎么样,差别不大吧。不知不觉就会 ES6 Promise 了!
ES6 的 Promise
上面已经把 ES6 的 Promise 带出来了,现在只需要把常用方法列出来作为参考即可
注意,小写的
promise
表示Promise
对象
-
new Promise(executor)
,产生一个新的 Promise 对象executor(resolve, reject)
executor
、resolve
和reject
均为函数,在executor
中,正确处理调用resolve()
返回数据,异常处理直接throw new Error(...)
或调reject()
返回数据。 -
Promise.resolve(data)
,产生 Promise 对象并resolve
Promise.reject()
,产生 Promise 对象并reject
-
promise.then(onResolve, onReject)
,然后……继续处理 promise.catch(onReject)
,project.then(null, onReject)
的语法糖,和 jQuery 的promise.fail()
差不多(但不同)。
参考

低调大师中文资讯倾力打造互联网数据资讯、行业资源、电子商务、移动互联网、网络营销平台。
持续更新报道IT业界、互联网、市场资讯、驱动更新,是最及时权威的产业资讯及硬件资讯报道平台。
转载内容版权归作者及来源网站所有,本站原创内容转载请注明来源。
- 上一篇
对缓存击穿的一点思考
前言 缓存(内存 or Memcached or Redis.....)在互联网项目中广泛应用,本篇博客将讨论下缓存击穿这一个话题,涵盖缓存击穿的现象、解决的思路、以及通过代码抽象方式来处理缓存击穿。 什么是缓存击穿? 上面的代码,是一个典型的写法:当查询的时候,先从Redis集群中取,如果没有,那么再从DB中查询并设置到Redis集群中。 注意,在实际开发中,我们一般在缓存中,存储的数据结构是JSON。(JDK提供的序列化方式效率稍微比JSON序列化低一些;而且JDK序列化非常严格,字段的增减,就很可能导致反序列失败,而JSON这方面兼容性较好) 假设从DB中查询需要2S,那么显然这段时间内过来的请求,在上述的代码下,会全部走DB查询,相当于缓存被直接穿透,这样的现象就称之为“缓存击穿”! 避免缓存击穿的思路分析 加synchronized? 如果synchronized加在方法上,使得查询请求都得排队,本来我们的本意是让并发查询走缓存。也就是现在synchronized的粒度太大了。 缩小synchronized的粒度? 上面代码,在缓存有数据时,让查询缓存的请求不必排队,减小了同...
- 下一篇
无线网络部署与规划要点及案例分享
2016年自己跟小伙伴一起搞的一个小活,客户是一家软件开发公司,专门做移动运动APP的,公司老板和一帮IT男对无线提出了比较高的要求,设备用的是国产"菊花"厂设备,当时对无线的很多概念仅仅停留在“知道”层面,理解的还不是很深入,研究了一个通宵交付给客户以后,自己当时小结了一下,翻出来,晒一晒,作为一个纪念。 无线网络部署白皮书 一、首先明确几个重要概念 WLAN 网络架构 WLAN网络架构分有线侧和无线侧两部分,有线侧是指AP上行到Internet的网络使用以太网协议。无线侧是指STA到AP之间的网络使用802.11协议。无线侧接入的WLAN网络架构为集中式架构。 集中式网络架构 集中式架构又称为瘦接入点(FIT AP)架构。在该架构下,通过AC集中管理和控制多个AP,如下图所示。 在集中式架构下,所有无线接入功能由AP和AC共同完成: AC集中处理所有的安全、控制和管理功能,例如移动管理、身份验证、VLAN划分、射频资源管理和数据包转发等。 FIT AP完成无线射频接入功能,例如无线信号发射与探测响应、数据加密解密、数据传输确认等。 A P和AC之间采用CAPWAP协议进行通讯,AP...
相关文章
文章评论
共有0条评论来说两句吧...
文章二维码
点击排行
推荐阅读
最新文章
- SpringBoot2整合MyBatis,连接MySql数据库做增删改查操作
- CentOS8安装Docker,最新的服务器搭配容器使用
- CentOS7编译安装Gcc9.2.0,解决mysql等软件编译问题
- SpringBoot2整合Redis,开启缓存,提高访问速度
- SpringBoot2编写第一个Controller,响应你的http请求并返回结果
- Jdk安装(Linux,MacOS,Windows),包含三大操作系统的最全安装
- Hadoop3单机部署,实现最简伪集群
- Docker使用Oracle官方镜像安装(12C,18C,19C)
- MySQL8.0.19开启GTID主从同步CentOS8
- Windows10,CentOS7,CentOS8安装MongoDB4.0.16