Promise作为在实际应用中或者面试中都经常出现的概念,掌握其基本用法很必要。后续会输出Promise相关的手写实现,那么我们就必须要掌握其方法的参数和返回值,此文按照阮一峰的ES6中关于Promise的讲解来整理!
理论简介
Promise:翻译过来就是"承诺"。是异步编程的解决方案,从语法上说,它是一个对象,是一个构造函数,用来生成实例。
Promise的两个特点:
- 对象的状态不受外界影响,只有异步操作的结果可以更改其状态。
- 状态一旦改变,则不会再次发生改变。也就是状态只能改变一次。
说到Promise的状态,他有三种状态:
- 进行中:pending
- 已成功:fulfilled
- 已失败:rejected
因为状态只可以改变一次,所以变化过程就只有如下两种情况:
- pending ->fulfilled
- pending->rejected
只要发生以上任一种情况,状态就凝固了不会再次变化,会一直保持这个结果,我们称这个状态为resolved(已定型)。
Promise的缺点:
- 无法取消Promise,新建后就会执行,无法中途取消
- 如果不设置回调函数,Promise内部发生的错误,不会反应到外部。也就是无法在try...catch(err){}中捕获到错误
- 当处于pending状态的时候无法得知目前在哪一个状态
基本用法
分析用法,主要从入参和返回值入手。
Promise对象作为构造函数,接受一个函数作为参数,该函数的两个参数分别是resolve和reject。这两个参数是函数类型,是javascript引擎提供的,不用自己部署,作用是更改Promise的状态。Promise新建后就会立即执行
resolve:在操作成功时调用。将状态从"未完成"->"完成",即pending->fulfilled;
reject:在操作失败时调用。将状态从"未完成"->"失败",即pending->rejected;
一般来讲,调用resolve或者reject之后并不会阻断后续代码的执行,但是从设计上来讲,后续的代码应该放到.then中执行,所以,最好在resolve或者reject的前面加上return语句。
//生成Promise实例
const promise = new Promise(function(resolve,reject){
//逻辑代码
if(异步操作成功){
resolve(value)
}else{
reject(reason)
}
})
Promise.prototype.then
入参:(成功状态下的处理函数,失败状态下的处理函数)
**返回值:**新的Promise实例。
Promise的实例生成后,可以用then方法分别指定“完成”状态和“失败”状态的回调函数,这两个函数都接受Promise对象传出的值作为参数。then方法返回一个新的Promise实例。
promise.then(function(value){
//成功状态下的处理函数
//如果resolve时传递出来的参数value是正常的值,那么这里接受到的就是正常的值,
//但是如果是另外一个Promise实例的话,事情会是另外的样子,见下文
},function(reason){
//失败状态下的处理函数
//reject一般会传递Error对象的实例
});
如上文所提,如果在resolve时,传递给then的参数是另一个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('success',result),
reason => console.log('failed',reason)
)
是的,打印出来的是failed Error: fail!做对了吗?因为如果resolve方法将另一个Promise实例,也就是p1,作为返回值,那么p1的状态就会传递给p2,也就是说p1的状态决定了p2的状态。这里需要注意一下,如果是p2执行reject的时候返回了另一个Promise实例,则不会出现这种现象。
因为then方法返回的仍旧是一个Promise实例,因此then可以采用链式调用。前一个then的返回结果,将会被作为参数传入到下一个then的回调函数中。如果前一个then返回的还是Promise对象,那么下一个then将会等待其状态变化,然后根据其状态结果来决定是执行resolved还是rejected的回调函数。
Promise.prototype.catch
入参:(处理错误的函数)
**返回值:**新的Promise实例
**应用场景:**Promise内部运行中抛出错误 或者 调用reject。
“Promise内部会吃掉错误”就是我们上文提到过Promise内部发生的错误,不会反应到外部。也就是Promise内部的逻辑如果发生错误,Promise外部的逻辑会继续执行,并不会退出进程。并且Promise的错误并不会被try/catch处理到。如果要处理Promise中发生的错误,推荐的做法就是.catch。
Promise对象的错误具有“冒泡”性质,会一直向后传递,直到其被捕获为止。
Promise.prototype.finally
**入参:**无
**返回值:**原来的值
不管Promise的状态是什么,都会执行。
Promise.all
入参:([p1,p2,p3]),接受一个具有Iterator接口的数据作为参数,常用数组,且返回的每个成员必须是Promise实例。如果不是,就先调用Promise.resolve()将参数转为Promise实例。
**返回值:**新的promise实例
const p = Promise.all([p1,p2,p3])
p的状态由传入的参数数组决定,分为两种情况:
- 数组中所有的实例的状态都变成fulfilled,p的状态才会变成fulfilled。此时p的回调函数then接受的参数是所有实例的返回值组成的数组。此处有一个注意点是:如果作为参数的实例自己定义了catch方法,那么即使此实例被rejected也不会触发Promise.all的catch方法。因为定义了catch之后,该方法返回的也是一个新的Promise实例,执行接受后,该实例的状态也是resolved状态的。
- 数组中只要有一个实例的状态变成了rejected,那么p的状态就会变成rejected。此时p的回调函数then接受的参数是第一个被reject的实例的返回值。
Promise.race
入参:([p1,p2,p3])同Promise.all
**返回值:**新的Promise实例
const p = Promise.race([p1,p2,p3])
参数中的实例只要有一个率先返回状态,p的状态就随之改变。而那个率先改变的Promise实例的返回值就会被传递到p的回调函数then中。
Promise.allSettled
入参:([p1,p2,p3])同Promise.all
**返回值:**新的Promise实例
const p = Promise.allSettle([p1,p2,p3])
参数中所有的实例,不论成功与否均返回结果后,p的状态才发生改变,且状态总是fulfilled。p的回调函数then接受到的参数是一个数组,数组中每个成员都是对象。每个对象都有一个status属性,该属性的值只可能是字符串'fulfilled'或者字符串'rejected'。fulfilled时对象有value属性,rejected时有reason属性,分别对应两种状态的返回值。
Promise.allSettled 对于那种只关心所有实例是否执行完毕,而并不关心执行结果的情况非常有用。
Promise.any
入参:([p1,p2,p3])同Promise.all
**返回值:**新的Promise实例
const p = Promise.any([p1,p2,p3])
只要有一个实例变成了fulfilled状态,那么p的状态就变成了fulfilled状态。
如果所有的实例都变成了rejected状态,那么p的状态就变成了rejected状态。
Promise.any 抛出的错误是一个AggregateError 实例。
Promise.resolve
**入参:**分为四种情况
- 没有参数:直接返回一个resolved状态的Promise对象
- 参数是一个Promise实例:不做任何修改,原地返回
- 参数是一个具有then方法的对象:会把这个对象转为Promise对象,并立刻执行这个then方法。运行结束后Promise对象的状态由这个then方法中的执行结果决定。如果有指定运行resolve或者reject,则Promise的状态变为指定的状态,否则一直是pending状态。
- 参数为其他对象或者不是对象:直接返回一个resolve状态的Promise对象。同时传入的参数会作为参数传递给Promise对象的回调函数.then中去。
Promise.reject
**入参:**不限
**返回值:**状态为rejected的新的Promise实例
Promise.reject()的参数会直接作为.then的返回值。