什么是Promise
Promise在英语里的意思是:“承诺”,它表示如A调用一个长时间的任务B的时候,B将返回一个“承诺”给A,A不用关心整个实施过程,继续做自己的任务;当B实施完成的时候,会通过A,并将执行A之间的预先约定的回调。
Promise 是异步编程的一种解决方案,比传统的解决方案——回调函数和事件监听——更合理和更强大。它由社区最早提出和实现,ES6 将其写进了语言标准,统一了用法,原生提供了Promise对象。
所谓Promise,简单说就是一个容器,里面保存着某个未来才会结束的事件(通常是一个异步操作)的结果。从语法上说,Promise 是一个对象,从它可以获取异步操作的消息。Promise 提供统一的 API,各种异步操作都可以用同样的方法进行处理。
Promise是一种封装和组合未来值的易于复用的机制
既然是对象,那么就有属性和方法
属性
Promise.length:长度属性,其值总是为 1 (构造器参数的数目).
Promise.prototype:表示 Promise 构造器的原型.
方法
基本用法
|
|
新建一个promise很简单,只需要new一个Promise对象即可。所以Promise本质上就是一个函数,它接受一个函数作为参数,并且会返回promise对象,这就给链式调用提供了基础。
三种状态
其实Promise函数的使命,就是构建出它的实例,并且负责帮我们管理这些实例。而这些实例有以下三种状态:
- pending: 初始状态,位履行或拒绝
- fulfilled: 意味着操作成功完成
- rejected: 意味着操作失败
Promise States
A promise must be in one of three states: pending, fulfilled, or rejected.
- When pending, a promise:
- may transition to either the fulfilled or rejected state.
- When fulfilled, a promise:
- must not transition to any other state.
- must have a value, which must not change.
- When rejected, a promise:
- must not transition to any other state.
- must have a reason, which must not change.
pending 状态的 Promise对象可能以 fulfilled状态返回了一个值,也可能被某种理由(异常信息)拒绝(reject)了。当其中任一种情况出现时,Promise 对象的 then 方法绑定的处理方法(handlers)就会被调用,then方法分别指定了resolve方法和reject方法的回调函数
简单的示例:
|
|
用Promise封装ajax
|
|
then
每一个 promise 都会提供给你一个 then() 函数 (或是 catch(),实际上只是 then(null, ...) 的语法糖)。它接收两个函数作为参数,then(onFilfulled, onRejected)。onFilfulled是成功的操作,onRejected是失败的操作。我们在 then() 函数内部可以做三种事情:
- return 另一个promise
- return 一个同步的值(或者undefined)
- throw一个同步异常
return另一个promise
|
|
|
|
返回一个同步值(或者undefined)
|
|
抛出同步异常
|
|
|
|
为什么使用Promise
异步不能使用try…catch
try…catch当然很好,但是无法跨异步操作工作。
|
|
error-first
|
|
捕获成功了! 但是我们接着往下看
|
|
上面的代码有什么问题呢? 很明显,错误没有被捕获到
只有在try里面调用同步的立即成功或失败的情况下,这里的try…catch才会工作。如果try里面还有异步完成函数,其中的任何异步错误都无法捕捉到。
多级error-first回调交织在一起,再加上这些无所不在的if检查语句,都不可避免地导致了回调地狱的风险。
callback hell


链式调用
|
|
问题:Promise回调地狱
正确的姿势
|
|
Promise.prototype.then方法返回的是一个新的Promise对象,因此可以采用链式写法。
|
|
错误处理
|
|
比较上面两种写法,可以发现reject方法的作用,等同于抛出错误。
如果 Promise 状态已经变成resolved,再抛出错误是无效的。
|
|
catch
Promise 对象的错误具有“冒泡”性质,会一直向后传递,直到被捕获为止。也就是说,错误总是会被下一个catch语句捕获。
一般来说,不要在then方法里面定义 Reject 状态的回调函数(即then的第二个参数),总是使用catch方法。
|
|
吃掉错误
先看一段同步代码
|
|
再看Promise
|
|
一般总是建议,Promise 对象后面要跟catch方法,这样可以处理 Promise 内部发生的错误。catch方法返回的还是一个 Promise 对象,因此后面还可以接着调用then方法。
Promise.resolve
如果我们经常写下面这样的代码
|
|
那么我用Promise.resolve会更加简洁
|
|
Promise.resolve()常用于创建一个已完成的Promise,但是Promise.resolve(...)也会展开thenable值。在这种情况下,返回的Promise采用传入的这个thenable的最终决议值,可能是完成,也可能是拒绝。
|
|
- 如果向
Promise.resolve(…)传递一个非Promise、非thenable的立即值,就会得到一个用这个值填充的promise。
|
|
- 如果向
Promise.resolve(...)传递一个真正的Promise,就只会返回同一个promise
|
|
Promise.reject
创建一个已被拒绝的Promise的快捷方式是使用Promise.reject(),所以以下两个promise是等价的:
|
|
Promise.all
|
|
异步
|
|
那么我们怎么在Promise中使用forEach呢?
|
|
对Promise.all([...])来说,只有传入的所有promise都完成,返回promise才能完成。如果有任何promise被拒绝,返回的主promise就立即会被拒绝。如果完成的话,我们会得到一个数组,其中包含传入的所有promise完成值。对于拒绝的情况,我们只会得到一个拒绝promise的理由。
|
|
Promise.all([...])需要一个参数,是一个数组通常由Promise实例组成。从Promise.all[(...)]调用返回的promise会收到一个完成消息,这是一个由所有传入promise的完成消息组成的数组,与指定的顺序一致(与完成顺序无关)
严格来说,传给Promise.all([...])的数组中的值可以是Promise、thenable、甚至是立即值。就本质而言,列表中的每个值都会通过Promise.resolve(...)过滤,以确保要等待的是一个真正的Promise,所以立即值会被规范化为这个值构建的Promise。如果数组是空的,主Promise就会立即完成。
Promise.race
对于Promise.race(...)来说,一旦有任何一个Promise决议完成,Promise,race([...])就会完成,一旦有任何一个Promise决议为拒绝,它就会拒绝。
|
|
|
|
注意:如果向Promise.all([...])传入空数组,它会立即完成,但Promise.race([...])会挂住,且永远不会决议。
穿透
|
|
我们以为会输出'bar',实际上却输出'foo',这是为什么呢?上面我们说到then函数接收两个函数作为参数,但是如果我们不传函数作为参数会怎么样呢?
它实际上会将其解释为 then(null),这就会导致前一个 promise 的结果会穿透下面。
|
|
添加任意数量的 then(null),它依然会打印 foo。
正确姿势
|
|
记住: 永远在then()中传递函数
期待async/await
更加语义化
参考文档
你不知道的JavaScript:异步与性能 第三章-Promise
本文结束,感谢阅读。
本文作者:melody0z
本文链接:https://melodyvoid/JavaScript/Share-meeting--Promise.html
欢迎转载,转载请注明文本链接
