文 | Leigh,UPYUN 已得到受权
原文连接:http://t.cn/R403hc4javascript
在 JavaScript 这么多年发展中,尤为在前端领域框架层出不穷,解决方案也琳琅满目,Promise 这个思想也逐渐从一个框架层面的实现变成了 ES 的规范,而且愈来愈多的新 API 都在以 Promise 为基础制定。是时候来看看这个怪物了!前端
在 JavaScript 尤为是前端开发领域,Promise 已经存在很多时日了,有一些曾经广受好评的第三方库,例如 Q,when,Bluebird,RSVP,基本都曾名闻遐迩,尽管实现方式/API 不同,但它们大多都遵循着 Promises/A+ 这个‘事实规范’。而归入 ES 规范的 Promise API 也是创建在 Promises/A+ 基础上的。java
要注意的是,jQuery 虽然有一个相似 Promise 的 API,可是它的 Promise 其实是被称做为 Deferred 对象的一个东西,它跟 Promises/A+ 的规范是不太兼容。git
Promise 主要用来解决 JS 开发中的异步问题,而对异步的处理,社区中也有着很多的解决方案,最多的就是回调这种形式,好比,在 Node.js 中:es6
这种书写上的约定,很容易解决对异步操做的处理,但缺陷是,这样的写法,咱们没发直接在异步函数中进行 return 和 throw 操做,只能局限于回调的形式中,而且很容易引发回调金字塔的状况。Promise 则对异步处理和处理方法都作了规范和抽象,还给了开发者在异步代码中使用 return 和 throw 的能力。而这也是 Promise 存在的真正意义。继续阅读 domenic 的相关解释github
其中 executor 函数拥有两个参数 resolve 和 reject。resolve 用于确定 Promise,reject 用于否认它,咱们能够在相关的操做结束后来执行这两个函数。web
能够看到,在语法上看,Promise 仍是有点像回调函数那种形式的,囧。不过,和回调函数相比,它的主要不一样点在于:编程
一、每个 Promise 都保证能让咱们收到一个值,Promise 即是这个值的代理,而咱们无需关心它被建立的时间点,咱们更能够在任意时刻注册咱们的结果处理函数,即便这个 Promise 已经完成(resolved)了(这时候,所注册的处理函数会被当即执行)。api
二、每个 Promise 都只会被成功或失败一次,而且这个状态不会被改变。数组
一个 Promise,会拥有如下几个状态之一:
一、pending(等待):操做正在执行中。
二、fulfilled(完成):操做已经成功执行完毕。
三、reject(失败):操做执行失败。
某些文章中可能会说到 settled 这个词,settled 表明 Promise 不是 pending 的状态,即:要么是 fulfilled,要么是 reject。但他自己并非一个状态,只是为了说的时候方便。详见 domenic: States and Fates
Promise 的状态变化图以下:
建立
建立一个 Promise,直接的方法是直接 new Promise 建立一个,即上文中语法中所述同样。其接受一个 executor 函数做为参数传递,Promise 在 executor 函数中提供了 resolve 和 reject 两个函数供开发者根据实际结果来调用。
在 Promise 对象上,还有 Promise.all(iterable), Promise.race(iterable), Promise.reject(reason), Promise.resolve(value) 四个方法,这些方法也会返回 Promise 对象,所以,咱们也能够经过这些方法来直接建立一个方法,例如 var p = Promise.resolve('I am a Promise!');
消费
Promise 一旦建立,咱们就能够把它当作一个值来传递,由于 Promise 就是对一个将来会获得的值的表明。所以,你能够将它从函数中返回(return),也能够当作参数来传递到须要的地方,就像传递一个普通的值同样。
消费 Promise,即对 Promise 的所表明的值进行一系列的处理。这里,咱们可使用 .then() 方法。
.then() 方法能够接受两个函数做为参数,第一个参数的函数会在 Promise 完成(fulfilled)的时候被调用,而第二个参数的函数则在 Promise 失败 (rejected)的时候调用。这两个参数都是能够被省略的:
每一个 Promise 实例还有一个 .catch() 方法,主要用于对错误的处理,对于 .catch() 的方法理解,其实很简单,它就至关于 .then() 省略第一个参数:
上述两种形式对错误的处理是等效的,当 Promise 失败的时候,两段代码的执行结果同样。不过,通常状况下,更加推荐使用 .catch() 方法来处理错误,由于 catch 自己名字更加直接易懂,而且,在链式调用 Promise 的时候,只须要在调用链最末端使用一个 .catch() 便可。
在 Promise 中,当直接 throw 一个异常的时候,将会使该 Promise 置为 rejected 状态,这里须要注意的是,在 Promise 链中抛出异常,仅仅会将抛出异常所在的 Promise 置为 rejected,而不会影响原始的 Promise,好比:
处理多个异步操做
Promise.all()
在不少时候,咱们都但愿多个异步操做可以并行,但不少状况下,咱们又但愿能在这多个异步操做所有都完成后再对结果进行处理,这个时候,咱们就会用到 Promise.all(),这个方法接受一个 Promise 数组做为参数,且会在全部 Promise 完成后执行一次回调 .then(),在这里,全部数组中的 Promise 都是并行方式执行的,以下:
注意:只要一旦有一个 promise 失败,.catch() 中的逻辑就会被执行。
Promise.all() 在一些大型 web app 中,将会颇有用,由于大多良好的 API 设计都是以数据维度划分,产品的某一个功能,可能会涉及到多个 API 的数据联动,这时候,使用 Promise.all() 便可方便的解决。
Promise.race() 它一样接受一个 Promise 数组做为参数,顾名思义,'race' 即比赛,想象一下,在平时百米竞赛时候,一旦有人率先冲过终点,那变产生了一个冠军,Promise.race() 便是在数组中的任意一个 Promise 完成后,便会触发 .then() 的逻辑,固然,这里数组中的 Promise 也是并行的:
Promise.race() 的经典使用场景是某个服务有多个互备的形式,而咱们须要尽快的拿到结果。
Series
并行很容易,可是 Promise 并无一个直接使用的串行的方法,固然,结合一些函数式编程的方法,咱们能够很简单的自行实现一下:
Promise 最近变得愈来愈火,不管前端领域,例如:Fetch API,Battery API,以及还未完成的 ServiceWorker API 等,甚至在 Node.js 社区中,人气很高的 co,以及 koa 新版本都将 thunk 的形式改成了 Promise,在实际生产中投入使用,阻力也不会太大。
ES 规范
MDN 文档
Can I Use 兼容性参考
es6-promise polyfill
欢迎关注 UPYUN 微信公众号( ID : upaiyun ),咱们每周都会分享高质量的原创技术文章。