这是我参与8月更文挑战的第3天,活动详情查看:8月更文挑战
Promise的含义
Promise是异步编程的一种解决方案,主要解决了传统回调函数的回调地狱问题。es6将Promise写进了语言标准,统一了用法,原生提供了 Promise 对象。
特点
- Promise对象的状态不受外界影响。三种状态:
pending(进行中)fulfilled(已成功rejected(已失败)
- 一旦状态改变,就不会再变,任何时候都能得到这个结果。
- 状态只能从
pedding变为fulfilled,或者从pedding变为rejected
- 状态只能从
缺点
- 一旦
新建Promise它就会立即执行,无法中途取消。(也就是说 new Promise()会立即执行,此处会涉及到EventLoop的面试考题) - 如果不设置回调函数,Promise内部抛出的错误,不会反应到外部。
- 当处于pedding状态时,无法得知目前进展到哪一个阶段(刚刚开始还是即将完成)。
基本用法
- Promise对象是一个构造函数,用来生成Promise实例。
- Promise构造函数
接收一个函数作为参数。 - 而作为参数的这个函数又接收两个参数
resolve和reject。 resolve和reject也是两个函数,由javaScript引擎,不用自己部署。 知道了Prmise构造函数的这些特点,那么就可以尝试实例化一个promise
const promise = new Promise((resolve, reject) => {
if(/*异步操作成功*/) {
resolve(value)
} else {
reject(error)
}
})
resolve 函数的作用:,将Promise对象的状态从pedding变为resolved,在异步操作成功时调用,并将异步操作的结果,作为参数传递出去。reject函数的作用:将Promise对象的状态 从 pedding 变为 rejected,在异步操作失败时调用,并将异步操作报出的错误,作为参数传递出去。- Promise实例生成之后,可以使用其原型链上的then方法,分别指定 resolved状态和rejected状态的回调函数。
- then方法接收两个参数,分别是成功的回调和失败的回调。成功的回调和失败的回调函数都接收Promise对象传出的值作为参数。(也就是我们在new Promise时 resolve和reject方法通过参数抛出的值)
promise.then((value) => {
// 此方法是成功的回调
console.log(value)
}, (error) => {
// 此方法是失败的回调
console.log(error)
})
注意的点
- 如果一个promise的resolve接收了另一个promise作为参数,即一个异步操作的结果是返回另一个异步操作。如下所示:
const p1 = new Promise(function (resolve, reject) {
setTimeout(() => reject(new Error('fail')), 3000)
})
const p2 = new Promise(function (resolve, reject) {
setTimeout(() => resolve(p1), 1000)
})
p2
.then(result => console.log(result))
.catch(error => console.log(error))
// Error: fail
将p1作为参数传递给p2,此时p1的状态决定了p2的状态。如果p1的状态是pending,那么p2的回调函数就会等待p1的状态改变;如果p1的状态已经是resolved或者rejected,那么p2的回调函数将会立刻执行。
上述代码中,正常来说p2在1秒之后会执行resolve,将状态变为resolved,但是我们可以看到p2的回调函数中resolve接收了p1作为参数,此时p2的状态就不是它自己说了算的,而是决定于p1的状态,而p1此时还处于pedding状态,那么此刻的p2需要等待p1的状态变更,再2秒之后p1调用了reject方法,状态变成了rejected,此时p2的回调函数立即执行,状态变成了rejectd,调用catch最终输出fail。
- resolve或者reject执行之后并不会终结Promise的参数函数的执行。也就是说,resolve()之后的代码依旧会执行,并且要先于resolve()输出结果.
- resolved的Promise是在本轮事件循环的末尾执行。
- 通常建议以下写法:
new Promise((resolve, reject) => {
return resolve(1);
// 后面的语句不会执行
console.log(2);
})
Promise.prototype.then()
then方法是定义在原型对象Promise.prototype上的。- then方法的作用是为Promise实例添加状态改变时的回调函数。
then方法接收两个可选参数:- 成功的回调函数
- 失败的回调函数
- then方法
返回的是一个 新的Promise实例。所以可以进行链式调用。
Promise.prototype.catch()
- 同样的catch也是定义在原型对象Promise.prototype上的。
- Promise.prototype.catch()是 .then(null, rejection) 或 .then(undefined, rejection)的别名,用于指定发生错误时的回调函数。
then()方法指定的回调函数,如果运行中抛出错误,也会被catch()方法捕获。- Promise对象的错误具有冒泡性质,会一直向后传递,直到被捕获为止。也就是说,错误总是会被下一个catch语句捕获。
如果没有使用catch()方法指定错误处理的回调函数,Promise对象抛出的错误是不会传递到外层代码的,既不会有任何反应。Promise 内部的错误不会影响到 Promise 外部的代码,通俗的说法就是“Promise 会吃掉错误”。看下面的例子:
const someAsyncThing = function() {
return new Promise(function(resolve, reject) {
// 下面一行会报错,因为x没有声明
resolve(x + 2);
});
};
someAsyncThing().then(function() {
console.log('everything is great');
});
setTimeout(() => { console.log(123) }, 2000);
// Uncaught (in promise) ReferenceError: x is not defined
// 123
上述代码中,最终promise内部报错之后 后续代码还是会输出123,可见 Promise 内部的错误不会影响到 Promise 外部的代码。
Promise.prototype.finally()
- 同样的finally也是定义在原型对象Promise.prototype上的。
- finally()方法用于指定不管Promise对象最后的状态如何,都会执行的操作。也就是说finally方法总是会执行。
finally方法的回调函数不接受任何参数。finally方法里面的操作,应该是与状态无关的,不依赖于 Promise 的执行结果。finally本质上是then方法的特例。
简单实现 finally
Promise.prototype.finally = function (callback) {
let P = this.constructor;
return this.then(
value => P.resolve(callback()).then(() => value),
reason => P.resolve(callback()).then(() => {throw reason})
)
}
Promise.all()
Promise.all()方法用于将多个 Promise 实例,包装成一个新的 Promise 实例。
const p = Promise.all([p1, p2, p3]);
Promise.all()接收一个数组作为参数。也可以不是数组,但是必须具有Iterator接口,且返回的每个成员都是Promise实例。- 只有
p1、p2、p3的状态都变成fulfilled,p的状态才会变成fulfilled,此时p1、p2、p3的返回值组成一个数组,传递给p的回调函数。 - 只要
p1、p2、p3之中有一个被rejected,p的状态就变成rejected,此时第一个被reject的实例的返回值,会传递给p的回调函数。 - 如果作为参数的 Promise 实例,自己定义了
catch方法,那么它一旦被rejected,并不会触发Promise.all()的catch方法。
Promise.race()
Promise.race()方法同样是将多个 Promise 实例,包装成一个新的 Promise 实例。
const p = Promise.race([p1, p2, p3]);
- 只要
p1、p2、p3之中有一个实例率先改变状态,p的状态就跟着改变。那个率先改变的 Promise 实例的返回值,就传递给p的回调函数。 Promise.race()方法的参数与Promise.all()方法一样,如果不是 Promise 实例,就会先调用Promise.resolve()方法,将参数转为 Promise 实例,再进一步处理。
Promise.allSettled()
Promise.allSettled()方法接受一组 Promise 实例作为参数,包装成一个新的 Promise 实例。只有等到所有这些参数实例都返回结果,不管是fulfilled还是rejected,包装实例才会结束。- 该方法返回的新的 Promise 实例,一旦结束,状态总是
fulfilled,不会变成rejected。 - 状态变成
fulfilled后,Promise 的监听函数接收到的参数是一个数组,每个成员对应一个传入Promise.allSettled()的 Promise 实例。
Promise.any()
Promise.any()方法接受一组 Promise 实例作为参数,包装成一个新的 Promise 实例返回。- 只要参数实例有一个变成
fulfilled状态,包装实例就会变成fulfilled状态;如果所有参数实例都变成rejected状态,包装实例就会变成rejected状态。 Promise.any()抛出的错误,不是一个一般的 Error 错误对象,而是一个 AggregateError 实例。它相当于一个数组,每个成员对应一个被rejected的操作所抛出的错误。
Promise.resolve()
Promise.resolve()用于将现有对象转为Promise对象。
Promise.resolve('foo')
// 等价于
new Promise(resolve => resolve('foo'))
Promise.resolve()方法的参数分成四种情况:- 参数是一个Promise实例:如果参数是 Promise 实例,
Promise.resolve将不做任何修改、原封不动地返回这个实例。 - 参数是一个thnable对象:(
thenable对象指的是具有then方法的对象),Promise.resolve()方法会将这个对象转为 Promise 对象,然后就立即执行thenable对象的then()方法。 - 参数不是具有then()方法的对象,或根本不是对象:如果参数是一个原始值,或者是一个不具有
then()方法的对象,则Promise.resolve()方法返回一个新的 Promise 对象,状态为resolved。 - 不带任何参数:
Promise.resolve()方法允许调用时不带参数,直接返回一个resolved状态的 Promise 对象。
- 参数是一个Promise实例:如果参数是 Promise 实例,
Promise.reject()
Promise.reject(reason)方法也会返回一个新的 Promise 实例,该实例的状态为rejected。Promise.reject()方法的参数,会原封不动地作为reject的理由,变成后续方法的参数。
本文参考自:es6入门