什么是 Promise
我们都知道 JavaScript 是一门单线程异步的语言,实现异步我们传统的方式是使用回调函数和事件,但是随着前端的发展,该模式面临着两个问题:
- 回调地狱:某个异步操作需要等待之前的异步操作完成,无论用回调还是事件,都会陷入不断的嵌套
- 异步之间的联系:某个异步操作要等待多个异步操作的结果,对这种联系的处理,会让代码的复杂度剧增
Promise
是异步编程的一种解决方案,比传统的解决方案——回调函数和事件,更合理和更强大。它由社区最早提出和实现,ES6 将其写进了语言标准,统一了用法,原生提供了 Promise
对象。
ES6的异步模型
在了解 Promise
之前,先来看看 ES 官方总结的一套异步处理模型,理解这套模型,能更好的理解 Promise
。
-
一件可能发生异步操作的事情,分为
unsettled
阶段和settled
阶段
unsettled
:未决阶段,表示事情还在进行前期的处理,并没有发生通向结果的那件事settled
:以决阶段,表示事情已经有了一个结果,结果无论好坏,整件事无法逆转
事情总是从
unsettled
逐步发展到settled
并且,未决阶段拥有控制何时通向已决阶段的能力 -
一件事被划分为三种状态:
pending
、resolved
、rejected
pending
:表示这件事情还在挂起(最终的结果还没出来)resolved
:表示整件事情已经出现结果,并是一个可以按照正常逻辑进行下去的结果rejected
:表示整件事情已经出现结果,并是一个无法按照正常逻辑进行下去的结果,通常用于表示有一个错误
既然未决阶段有权力决定事情的走向,因此,未决阶段可以决定事情最终的状态
- 把事情变为
resolved
状态的过程叫做:resolve
,推向该状态时,可能会传递一些数据 - 把事情变为
rejected
状态的过程叫做:reject
,推向该状态时,可能会传递一些数据,通常为错误信息
-
当事情达到
settled
后,通常需要进行后续处理,不同的已决状态,决定了不同的后续处理resolved
状态:这是一个正常的settled
,后续处理表示为thenable
rejected
状态:这是一个非正常的unsettled
,后续处理表示为catchable
后续处理可能有多个,因此会形成作业队列,这些后续处理会按照顺序,当状态到达后依次执行
所谓的Promise
就是整个过程
基本使用
Promise
对象是一个构造函数,用来生成 Promise
实例,未决阶段的处理会立即执行(Promise
新建后就会立即执行)
resolve
和 reject
均可以传递最多一个参数,表示推向状态的数据
const promise = new Promise((resolve, reject)=>{
// 未决阶段的处理立即执行
if (Math.random() < 0.5) {
resolve(1)
} else {
reject(new Error('出错了'))
}
});
通过 Promise.prototype.then
注册 resolved 和 rejected 状态的函数
promise.then(data => {
// thenable 处理函数
},err => {
// catchable 处理函数
})
通过 Promise.prototype.catch
注册 rejected 状态的函数
promise.catch(err => {
// catchable 处理函数
})
thenable 和 catchable 函数都可以注册多个,执行时会按照注册顺序执行
注意点
-
运行到注册
thenable
/catchable
函数时,如果Promise
的状态已决,该函数会立即执行;如果是未决,则会加入到作业队列,等待到达resolved
/rejected
状态后再执行 -
thenable
/catchable
函数是异步的,也就是说第二点的立即执行说的不准确,应该是放入事件队列的微任务队列等待同步函数执行完再执行const promise = new Promise((resolve, reject)=>{ setTimeout(() => { resolve(1) }, 1000); }); console.log(promise); promise.then(data => console.log(data)); console.log(promise); // pending // pending // 1
-
在未决阶段的处理函数中,如果发生未捕获的错误,会将状态推向
rejected
,并会被catchable
函数捕获const promise = new Promise((resolve, reject)=>{ throw new Error('出错了') }); console.log(promise); promise.catch(err => console.log(err)); // rejected // Error: 出错了
-
一旦状态推向了已决阶段,无法再对状态做任何更改,也就是说,一旦状态变为
resolved
/rejected
再执行reject
/resolve
函数或者抛出错误是没有办法再改变状态的const promise = new Promise((resolve, reject)=>{ throw new Error('出错了'); resolve(1); }); console.log(promise); promise.then(data => console.log(data), err => console.log(err)); // rejected // Error: 出错了
-
如果
resolve
的参数是另一个Promise
,则该Promise
的状态和参数由那个Promise
参数的状态决定const promise1 = new Promise((resolve, reject) => { reject(1); }) const promise2 = new Promise((resolve, reject) => { resolve(promise1); }) promise2.then(data => console.log(data), err => console.log('err', err)); console.log(promise1 === promise2) // false // err 1
-
如果
reject
参数的另一个参数是Promise
,则该 Promise 的状态为rejected
,err
参数为传入的参数Promise
const promise1 = new Promise((resolve, reject) => { reject(1) }) const promise2 = new Promise((resolve, reject) => { reject(promise1) }) promise2.then(data => console.log(data), err => console.log('err', err)) // err Promise {<rejected>: 1}
Promise 的串联
Promise.prototype.then
和 Promise.prototype.catch
函数可以注册已决阶段处理的函数,并且他们都返回一个新的 Promise
,该 Promise
的状态由当前的 Promise
的状态决定
-
只有当当前的
Promise
的状态已决并且处理函数执行完该Promise
的状态才会从pendding
变为resolved
/rejected
,并且thenable
/catchable
函数的返回值就是这个新的Promise
的数据 -
无论这个
Promise
是then
函数还是catch
返回的,只要该处理函数没有抛出未捕获的错误,新的Promise
的状态一定是resolved
const promise1 = new Promise((resolve, reject) => { setTimeout(() => { resolve(1) }, 1000); }) const promise2 = promise1.then(data => { console.log(data); return 2; }) console.log(promise1, promise2) setTimeout(() => { console.log(promise1, promise2); }, 1000); //pending pending // 1 // fulfilled 1 fulfilled 2
-
如果前面的
Promise
的后续处理,返回的是一个Promise
,则返回的新的Promise
状态和后续处理返回的Promise
状态保持一致,并且数据也一致const promise1 = new Promise((resolve, reject) => { resolve(1) }) const promise2 = promise1.then(data => { console.log(data); return new Promise((resolve, reject) => { reject(2) }) }) console.log(promise1, promise2) setTimeout(() => { console.log(promise2); }, 1000); // fulfilled 1 pending // 1 // rejected 2
-
catchable
函数可以捕获前面所有未捕获的错误new Promise((resolve, reject) => { resolve(1); }).then(data => { // ... }).catch(err => { //... }) // 可以捕获前面所有的错误
其他API
-
Promise.prototype.finally
[ ES 2018 ]和
then
,catch
一样使用,但是finally
注册函数没有参数,并且无论Promise
是resolved
还是rejected
都会执行 -
Promise.resolve
该方法返回一个
resolved
状态的Promise
,传递的数据作为状态数据相当于:
new Promise((resolve, reject) => { resolve(err); });
如果传递的数据是一个
Promise
,则直接返回该Promise
const promise1 = new Promise((resolve, reject) => { resolve(1) }) const promise2 = Promise.resolve(promise1) console.log(promise1 == promise2) // true
如果传入的参数是一个
thenable
对象,会将这个对象转为Promise
对象,然后就立即执行thenable
对象的then()
方法thenable
对象指的是具有then
方法的对象const thenable = { then(resolve, reject) { console.log(1) resolve(42); } }; const p1 = Promise.resolve(thenable); p1.then(function (value) { console.log(value); }); // 1 // 42
-
Promise.reject
等同于
new Promise((resolve, reject) => { reject(err); });
-
Promise.all(iterable)
该方法返回一个
Promise
对象,该对象在iterable
参数对象里所有的Promise
对象都resloved
的时候才会resolved
,只要有一个rejected
该Promise
对象就会rejected
;该对象thenable
的参数为一个包含所有iterable
中Promise
的返回值,该对象的catchable
的参数为iterable
里第一个触发失败的Promise
对象的错误信息 -
Promise.race(iterable)
该方法返回一个
Promise
对象,该对象在iterable
参数对象里所有的Promise
对象都resloved
的时候才会resolved
,只要有一个rejected
该Promise
对象就会rejected
;该对象thenable
的参数为一个包含所有iterable
中Promise
的返回值,该对象的catchable
的参数为iterable
里第一个触发失败的Promise
对象的错误信息 -
Promise.race(iterable)
看名字就可以看出来(看哪个
Promise
先完成),当iterable
参数里的任意一个子Promise
成功或失败后;返回的Promise
马上也会用子Promise
的成功返回值或失败详情作为参数调用该对象相应的thenable
或catchable