ES6 和 jQuery 都有 Deffered 和 Promise,可是略有不一样。不过它们的做用能够简单的用两句话来描述javascript
在 jQuery 中html
var deferred = $.Deferred(); var promise = deferred.promise();
在 ES6 中java
var deferred = Promise.defer(); var promise= defered.promise;
MDN 宣布 Deferred 在 Gecko 30 中被申明为过时,不该该再使用,而应该用
new Promise()
来代替。关于new Promise()
将在后面说明。jquery
jQuery 中最经常使用的 Promise 对象是 $.ajax()
返回的,最经常使用的方法不是 then
,而是 done
、fail
和 always
。除了 $.ajax()
外,jQuery 也提供了 $.get()
、$.post()
和 $.getJSON()
等简化 Ajax 调用,它们返回的和 $.ajax()
的返回值同样,是个 Promise 对象。ajax
实际上
$.ajax()
返回的是一个 jqXHR 对象。但 jqXHR 实现了 jQuery 的 Promise 接口,因此也是一个 Promise 对象。json
done()
、fail()
和 always()
done()
添加 deferred.resolve()
的回调,fail()
添加 deferred.reject()
的回调。因此在 Ajax 调用成功的状况下执行 done()
添加的回调,调用失败时执行 fail()
添加的回调。但无论成功与否,都会执行 always()
添加的回调。api
这里 done()
、fail()
和 always()
都是以相似事件的方式添加回调,也就意味着,无论执行屡次次 done()
、fail()
或 always()
,它们添加的若干回调都会在符合的条件下依次执行。promise
通常状况下会这样执行 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 是这样的格式:markdown
{ "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()
。
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 就好 });
上面的代码太长,提炼一下关键部分(示意,不能运行)
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 带出来了,如今只须要把经常使用方法列出来做为参考便可
注意,小写的
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()
差很少(但不一样)。