什么是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
欢迎转载,转载请注明文本链接