Promise
解决了回调地狱的问题
设计到一个概念"事件循环",可以使用动画来了解
基本用法
先声明一个Promise
// 需要传递一个函数,函数里面会接收到两个参数,这两个参数是两个函数
const promise = new Promise(function(resolve, reject) {
// ... some code
if (/* 异步操作成功 */){
resolve(value); // 从“未完成”变为“成功”
} else {
reject(error); // 从“未完成”变为“失败”
}
});
然后使用then
方法
promise.then(function(value) { // 对应 resolve(value)
// success
}, function(error) { // 对应 reject(error),这个参数可以没有
// failure
});
Promise
新建后会立即执行,then
里面的回调将在本轮“事件循环”结尾执行
如果resolve
或者reject
函数中有参数,这个参数将会被传递给回调函数。
如果参数为另一个Promise
,那么当前Promise
的状态将由另一个Promise
决定
调用resolve
和reject
方法的时候,并不会终结Promise
参数中的函数的执行。
Promise.prototype.then()
then
方法为Promise
实例添加状态改变时的回调函数。
then
方法返回一个新的Promise
实例,注意是新的,所以可以链式调用。
如果前一个then
回调的返回值返回的也是一个Promise
,那么下一个then
会等待这个新的Promise
对象的状态发生变化。
Promise.prototype.catch()
Promise.prototype.catch
方法是.then(null, rejection)
或.then(undefined, rejection)
的别名,用于指定发生错误时的回调函数。
catch
可以捕获前面执行的错误,不管是有多少个then
,只要前面没有处理错误,都会被后面的catch
处理,所以说then
就不需要第二个参数了,所有的异常全部交给catch
来处理。
Promise
内部出现了错误,并不会影响Promise
外面代码的执行,Promise
会吃掉错误。
由于catch
依然返回一个Promise
,所以catch
方法后面还可以使用then
,但是如果后面的then
抛出的错误,就和前面的catch
没有关系了。
Promise.prototype.finally()
finally
方法用于指定不管 Promise
对象最后状态如何,都会执行的操作。该方法是 ES2018
引入标准的。
finally
的回调函数不接受任何参数
Promise.all()
可以理解为这个方法是&&
运算符
Promise.all()
方法用于将多个 Promise
实例,包装成一个新的 Promise
实例。
传入一个数组或者Iterator
作为参数,数组中应该全部都是Promise
实例,如果不是Promise
实例,就会自动转换成Promise
实例。
当数组中的Promise
状态全部变成fulfilled
状态的时候,Promise.all()
才会变成fulfilled
,then
的回调接收到一个数组,数组里面是这个Promise
数组的返回值。
当数组中有一个rejected
,Promise.all()
就变成了rejected
,第一个reject
的返回值,会传递给回调函数(catch
或者then
的第二个参数)。
如果说,数组里面有一个Promise
已经处理了catch
,那么这个Promise
出现错误的时候就不会交给Promise.all()
来处理了,因为处理完catch
的Promise
已经变成了fulfilled
状态。
Promise.race()
Promise.race()
方法同样是将多个 Promise
实例,包装成一个新的 Promise
实例。
Promise.race()
这个方法的回调中只能接收到率先改变的哪个Promise
,不管变成什么状态,只要它率先改变了状态,就会调用回调。
利用这个特性可以做操作超时:
Promise.race([
new Promise((resolve, reject) => void setTimeout(x => resolve("访问网络资源"), 6000)),
new Promise((resolve, reject) => void setTimeout(x => reject("访问网络资源超时..."), 1000))
])
.then(x => void console.log("访问成功", x))
.catch(x => void console.log("访问失败", x));
// 访问失败 访问网络资源超时...
Promise.allSettled()
不关心异步操作的结果,只关心这些操作有没有结束。这时,Promise.allSettled()
方法就很有用
Promise.allSettled()
方法接受一组 Promise
实例作为参数,包装成一个新的 Promise
实例。只有等到所有这些参数实例都返回结果,不管是fulfilled
还是rejected
,包装实例才会结束。该方法由 ES2020 引入。
这个方法只会调用resolve
回调,不会调用reject
回调函数中接受到的参数是一个对象数组,对象的属性如下:
status
:属性的值只可能是字符串fulfilled
或字符串rejected
value
:fulfilled
时,对象有value
属性
reason
:rejected
时有reason
属性
Promise.any()
这个可以理解为是||
运算符
该提案已经进入第三阶段
参数也是一个数组,只要参数实例有一个变成fulfilled
状态,包装实例就会变成fulfilled
状态;如果所有参数实例都变成rejected
状态,包装实例就会变成rejected
状态
这个方法抛出的错误,不是一个一般的错误,是AggregateError
实例,相当于一个数组,存储了每一个Promise
被rejected
后抛出的错误。
Promise.resolve()
有时需要将现有对象转为 Promise
对象,Promise.resolve()
方法就起到这个作用
转换成Promise
对象有以下几种参数:
参数本身就是一个Promise实例:不变化
参数是一个thenable对象:也就是说这个对象里面有then
方法,会先将这个对象转换成Promise
对象,然后执行对象的then
方法。
参数是一个普通的原始值:会直接将原始值传递给then
的回调。
不带参数:直接返回一个resolved
状态的Promise
对象。
then
回调中的语句是在本轮事件循环结束的时候执行,不会到下一轮事件循环中执行。
Promise.reject()
Promise.reject(reason)
方法也会返回一个新的 Promise
实例,该实例的状态为rejected
。
Promise.reject()
方法的参数,会原封不动地作为reject
的理由,变成后续方法的参数,而不会转换成Promise
对象
Promise.try()
这也是一个新的提案
目的是让同步的方法也可以轻松的使用Promise
来包装,并且不会变成异步执行(本轮事件循环末尾执行),使用try
后,catch
方法不仅可以捕捉到异步的异常,也可以捕获同步的异常
微任务
使用setTimeout
这样的方法创建的任务会到下一轮事件循环中执行,但是then
方法中的回调属于微任务,他会到本轮事件循环末尾执行,也就是会直接追加到本轮事件循环,而不会到下一轮事件循环中执行。所以,then
的回调会永远早于setTimeout
执行。