开篇首先设想一个平常开发经常会遇到的需求:在多个接口异步请求数据,而后利用这些数据来进行一系列的操做。通常会这样去写:javascript
$.ajax({
url: '......', success: function (data) { $.ajax({ // 要在第一个请求成功后才能够执行下一步 url: '......', success: function (data) { // ...... } }); } });
这样的写法的原理是,当执行一些异步操做时,咱们须要知道操做是否已经完成,全部当执行完成的时候会返回一个回调函数,表示操做已经完成。java
使用回调函数的形式理解起来并不困难,可是实际的应用当中会有如下的缺点:jquery
在须要多个操做的时候,会致使多个回调函数嵌套,致使代码不够直观,就是常说的 Callback Hell。ajax
若是几个异步操做之间并无先后顺序之分(例如不须要前一个请求的结果做为后一个请求的参数)时,一样须要等待上一个操做完成再实行下一个操做。segmentfault
为了解决上述的问题,Promise 对象应运而生,在 EMCAScript 2015 当中已经成为标准。api
一个 Promise 对象能够理解为一次将要执行的操做(经常被用于异步操做),使用了 Promise 对象以后能够用一种链式调用的方式来组织代码,让代码更加直观。并且因为 Promise.all
这样的方法存在,可让同时执行多个操做变得简单。接下来就来简单介绍 Promise 对象。数组
首先来看一段使用了 Promise 对象的代码。浏览器
function helloWorld (ready) { return new Promise(function (resolve, reject) { if (ready) { resolve("Hello World!"); } else { reject("Good bye!"); } }); } helloWorld(true).then(function (message) { alert(message); }, function (error) { alert(error); });
jsFiddle(请用现代浏览器打开)babel
上面的代码实现的功能很是简单,helloWord
函数接受一个参数,若是为 true
就打印 "Hello World!",若是为 false
就打印错误的信息。helloWord
函数返回的是一个 Promise 对象。异步
在 Promise 对象当中有两个重要方法————resolve
和 reject
。
resolve
方法可使 Promise 对象的状态改变成成功,同时传递一个参数用于后续成功后的操做,在这个例子当中就是 Hello World!字符串。
reject
方法则是将 Promise 对象的状态改变为失败,同时将错误的信息传递到后续错误处理的操做。
上面提到了 resolve
和 reject
能够改变 Promise 对象的状态,那么它究竟有哪些状态呢?
Promise 对象有三种状态:
Fulfilled 能够理解为成功的状态
Rejected 能够理解为失败的状态
Pending 既不是 Fulfilld 也不是 Rejected 的状态,能够理解为 Promise 对象实例建立时候的初始状态
helloWorld 的例子中的 then
方法就是根据 Promise 对象的状态来肯定执行的操做,resolve 时执行第一个函数(onFulfilled),reject 时执行第二个函数(onRejected)。
helloWorld 的例子当中利用了 then(onFulfilld, onRejected)
方法来执行一个任务打印 "Hello World!",在多个任务的状况下then
方法一样能够用一个清晰的方式完成。
function printHello (ready) { return new Promise(function (resolve, reject) { if (ready) { resolve("Hello"); } else { reject("Good bye!"); } }); } function printWorld () { alert("World"); } function printExclamation () { alert("!"); } printHello(true) .then(function(message){ alert(message); }) .then(printWorld) .then(printExclamation);
上述例子经过链式调用的方式,按顺序打印出了相应的内容。then
可使用链式调用的写法缘由在于,每一次执行该方法时老是会返回一个 Promise 对象。另外,在 then
onFulfilled 的函数当中的返回值,能够做为后续操做的参数,所以上面的例子也能够写成:
printHello(true).then(function (message) { return message; }).then(function (message) { return message + ' World'; }).then(function (message) { return message + '!'; }).then(function (message) { alert(message); });
一样能够打印出正确的内容。
catch
方法是 then(onFulfilled, onRejected)
方法当中 onRejected
函数的一个简单的写法,也就是说能够写成then(fn).catch(fn)
,至关于 then(fn).then(null, fn)
。使用 catch
的写法比通常的写法更加清晰明确。
Promise.all
能够接收一个元素为 Promise 对象的数组做为参数,当这个数组里面全部的 Promise 对象都变为 resolve 时,该方法才会返回。
var p1 = new Promise(function (resolve) { setTimeout(function () { resolve("Hello"); }, 3000); }); var p2 = new Promise(function (resolve) { setTimeout(function () { resolve("World"); }, 1000); }); Promise.all([p1, p2]).then(function (result) { console.log(result); // ["Hello", "World"] });
上面的例子模拟了传输两个数据须要不一样的时长,虽然 p2 的速度比 p1 要快,可是 Promise.all
方法会按照数组里面的顺序将结果返回。
平常开发中常常会遇到这样的需求,在不一样的接口请求数据而后拼合成本身所需的数据,一般这些接口之间没有关联(例如不须要前一个接口的数据做为后一个接口的参数),这个时候 Promise.all
方法就能够派上用场了。
还有一个和 Promise.all
相相似的方法 Promise.race
,它一样接收一个数组,不一样的是只要该数组中的 Promise 对象的状态发生变化(不管是 resolve 仍是 reject)该方法都会返回。
最后是关于 Promise 对象的兼容性问题。
在浏览器端,一些主流的浏览器都已经可使用 Promise 对象进行开发,在 Node.js 配合 babel 也能够很方便地使用。
若是要兼容旧的浏览器,建议能够寻找一些第三方的解决方案,例如 jQuery 的 $.Deferred。
感谢你的阅读,有不足之处请为我指出。