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,而是 donefail 和 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)
executorresolve 和 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() 差不多(但不同)。

参考

通过 ES6 Promise 和 jQuery Deferred 的异同学习 Promise的更多相关文章

  1. jquery.Deferred promise解决异步回调

    我们先来看一下编写AJAX编码经常遇到的几个问题: 1.由于AJAX是异步的,所有依赖AJAX返回结果的代码必需写在AJAX回调函数中.这就不可避免地形成了嵌套,ajax等异步操作越多,嵌套层次就会越 ...

  2. javascript异步代码的回调地狱以及JQuery.deferred提供的promise解决方式

    我们先来看一下编写AJAX编码常常遇到的几个问题: 1.因为AJAX是异步的,全部依赖AJAX返回结果的代码必需写在AJAX回调函数中.这就不可避免地形成了嵌套.ajax等异步操作越多,嵌套层次就会越 ...

  3. JS魔法堂:jQuery.Deferred(jQuery1.5-2.1)源码剖析

    一.前言 jQuery.Deferred作为1.5的新特性出现在jQuery上,而jQuery.ajax函数也做了相应的调整.因此我们能如下的使用xhr请求调用,并实现事件处理函数晚绑定. var p ...

  4. 第三十三课:jQuery Deferred详解1

    之前我们讲了Mochikit Deferred,JSDeferred,现在讲jQuery Deferred.首先,我们先来讲下他们的区别: 在保存回调函数时,Mochikit Deferred(doj ...

  5. jQuery.Deferred(jQuery1.5-2.1)源码剖析

    jQuery.Deferred作为1.5的新特性出现在jQuery上,而jQuery.ajax函数也做了相应的调整.因此我们能如下的使用xhr请求调用,并实现事件处理函数晚绑定. var promis ...

  6. jQuery Deferred对象详细源码分析(-)

    本系列文章讲介绍这个Deferred东西到底拿来干什么,从1.5版本加进来,jQuery的很多代码都重写了.直接先上源码分析了,清楚了源码分析,下节将讲具体的应用 以及应用场景. 创建对象 var d ...

  7. jQuery Deferred和Promise的使用介绍:

    deferred对象是从jquery1.5.0引入的一个新对象,ES6也引入了Promise的正式规范. 抽象来说,deferreds 可以理解为表示需要长时间才能完成的耗时操作的一种方式,相比于阻塞 ...

  8. 使用 jQuery Deferred 和 Promise 创建响应式应用程序

    这篇文章,我们一起探索一下 JavaScript 中的 Deferred 和 Promise 的概念,它们是 JavaScript 工具包(如Dojo和MochiKit)中非常重要的一个功能,最近也首 ...

  9. JQuery的异步回调支持 - Promise、Deferred

    1.Deferred对象: 一般在函数内部进行声明,并在运行过程中改变其状态,例如成功或失败,最终返回Promise对象用于状态监听. 主要方法: Deferred.resolve(param...) ...

随机推荐

  1. hdu 2065

    ps:我的天...看网上各种难..对于我这个比较懒得人...我就找规律直接水过去了...前20一个循环,注意跳过第一轮的3个数就行..然后觉得比较坑的是,那个输入N,要用long long型... 代 ...

  2. android技巧(一):如何方便知晓当前Activity?如何管理应用中的Activity?如何最佳的启动一个Activity?

    1.如何方便知晓当前Activity? 可以不看代码根据当前界面就知道界面所在Activity的写法: 建立BaseActivity,继承自Activity,在BaseActivity的OnCreat ...

  3. eclipse working sets 视图 解决Other Projects不见问题

    请移步: http://note.youdao.com/yws/public/redirect/share?id=d54cac4232078f9acab551d62337a2d1&type=f ...

  4. 电子词典的相关子函数db.c程序

    整个电子词典是分块做的:包含的Dic_Server.c,Dic_Client.c,db.c,query.c,xprtcl.c,dict.h,xprtcl.h,dict.txt(单词文件) 下面是db. ...

  5. 实际项目中积累的一些关于事件的简单应用JS代码段(能力有限,不喜轻喷,23333)

    1:鼠标移入移出显示另一张图片 var yuanquan_1 = document.getElementById("yuanquan_1" );  yuanquan_1. onmo ...

  6. LPTHW 笨方法学习python 16章

    根据16章的内容作了一些扩展. 比如,判断文件如果存在,就在文件后追加,如不存在则创建. 同时借鉴了shell命令中类似 cat <<EOF > test的方法,提示用户输入一个结尾 ...

  7. C语言处理xml文件的库

    读取和设置xml配置文件是最常用的操作,试用了几个C++的XML解析器,个人感觉TinyXML是使用起来最舒服的,因为它的API接口和Java的十分类似,面向对象性很好. TinyXML是一个开源的解 ...

  8. mysql控制台操作

    显示表结构  : show create table  table_name 复制表:insert into table_name1 select * from table_name2

  9. SpringMVC 用http请求的Get和Post请求作为路由的方法的重载方式

    @Controller @RequestMapping("/messageProcessing") public class WechatPushController { @Aut ...

  10. viewPager动态加载listview数据

    废话不多说,先上效果图.(代码见附件) 代码是修改自某大神的,我做了很多修改,之前只能向右滑动,现在可以左右无限滑动,只要数据没加载完就可以一直滑动.过程不算复杂,代码主要的地方都有注释. 附件dem ...