JavaScript入门教程

JavaScript简介
JavaScript语法基础
JavaScript流程控制
JavaScript函数
面向对象编程
JavaScript事件
JavaScript DOM
正则表达式
JavaScript BOM
AJAX

专题分析

浏览器兼容性
JS优化
Web前端开发规范
编辑器推荐
总结和笔记

学习助手

对象参考手册
ECMAScript分析
数据中心
QQ交流群

Promise对象的合并

进度通知的存在并没有改变每个Promise 对象的最终状态为已执行或已拒绝这一事实。(否则,Promise 对象将永远保持挂起状态。)但为什么要这样呢?为什么不让Promise 对象随时变化成任意的状态,而
偏偏只有这两种状态呢?

这样设计Promise,其主要原因是程序员一直都在跟二进制打交道。我们这些码农非常清楚如何把1 和0 揉捏起来建成令人瞠目的逻辑之塔。Promise 如此强大的一个主要原因是,它允许我们把任务当成布尔量来处理。

Promise 对象的逻辑合并技术有一个最常见的用例:判定一组异步任务何时完成。假设我们正在播放一段演示视频,同时又在加载服务器上的一个游戏。我们希望这两件事一旦结束(对次序没有要求),就马上启动游戏。
  • 演示视频已经播放完毕。
  • 游戏已经加载完毕。

这两个进程各用一个Promise 对象来表示,我们的任务就是在这两个Promise 均已执行时启动游戏。我们如何做到这一点呢?

下面隆重介绍jQuery 的when 方法!
var gameReadying = $.when(tutorialPromise, gameLoadedPromise);
gameReadying.done(startGame);
when 相当于Promise 执行情况的逻辑与运算符(AND)。一旦给定的所有Promise 均已执行,就立即执行when 方法产生的Promise 对象;或者,一旦给定的任意一个Promise 被拒绝,就立即拒绝when 产生的Promise。

when 方法的绝佳用例是合并多重Ajax 调用。假设需要马上进行两次post 调用,而且要在这两次调用都成功时收到通知,这时就无需再为每次调用请求分别定义一个回调。
$.when($.post('/1', data1), $.post('/2', data2))
.then(onPosted, onFailure);
调用成功时,when 可以访问下辖的各个成员Promise 对象的回调参数,不过这么做很复杂。这些回调参数会当作参数列表进行传递,传递的次序和成员Promise 对象传递给when 方法时一样。如果某个成员Promise 对象提供多个回调参数,则这些参数会先转换成数组。

因此,要想根据赋予$.when 方法的所有成员Promise 对象获得全部回调参数,可能会写出像下面这样的代码(但笔者并不推荐这么做)。
$.when(promise1, promise2)
    .done(function(promise1Args, promise2Args) {
        // ...
    });
在这个例子中,如果执行promise1 时用到了一个参数'complete',执行promise2 时用到了3 个参数(1、2、3),则promise1Args 就是字符串'complete',promise2Args 就是数组[1,2,3]。

虽然有可能,但如果不是绝对必要,我们不应该自行解析when 回调的参数,相反应该直接向那些传递至when 方法的成员Promise 对象附加回调来收集相应的结果。
var serverData = {};
var getting1 = $.get('/1')
    .done(function(result) {serverData['1'] = result;});
var getting2 = $.get('/2')
    .done(function(result) {serverData['2'] = result;});
$.when(getting1, getting2)
    .done(function() {
        // 获得的信息现在都已位于serverData……
    });

函数的Promise用法

$.when 及其他能取用Promise 对象的jQuery 方法均支持传入非Promise 对象作为参数。这些非Promise 参数会被当成因相应参数位置已赋值而执行的Promise 对象来处理。例如
$.when('foo')
会生成一个因赋值'foo'而立即执行的Promise 对象。再譬如
var promise = $.Deferred().resolve('manchu');
$.when('foo', promise)
会生成一个因赋值'foo'和'manchu'而立即执行的Promise 对象。代码:
var promise = $.Deferred().resolve(1, 2, 3);
$.when('test', promise)
会生成一个因赋值'test'和数组[1,2,3]而立即执行的Promise 对象。(请记住,Deferred 对象传递多个参数给resolve 方法时,$.when会把这些参数转换成一个数组。)

这带来了一个问题:$.when 如何知道参数是不是Promise 对象呢?

答案是:jQuery负责检查$.when 的各个参数是否带有promise 方法,如果有就使用该方法返回的值。Promise 对象的promise 方法会直接返回自身。

正如3.2.2 节所述,jQuery 对象也可以有promise 方法,这意味着$.when 方法强行将那些带promise 方法的jQuery 对象转换成了jQuery 动画版Promise 对象。因此,如果想生成一个在抓取某些数据且已完成#loading 动画之后执行的Promise 对象,只需写下下面这样的代码:
var fetching = $.get('/myData');
$.when(fetching, $('#loading'));
只是请记住,必须要在动画开始之后再执行$.when 生成的那个Promise 对象。如果#loading 的动画队列为空,则立即执行相应的Promise 对象。