-
Promise 的含义
Promise
对象:
三种状态:pending(进行中)、fulfilled(已成功)和rejected(已失败)。
状态一旦确定则无法改变。
状态任意时候都可以得到,状态得到则Promise
对象的回调函数,就会立即得到这个结果
无法取消Promise
,一旦新建它就会立即执行(new
的promise
实例会立即执行)
如果不设置回调函数,Promise
内部抛出的错误,不会反应到外部 -
基本用法
var promise = new Promise(function(resolve, reject) { resolve(sucessInfo)//当执行成功时可调用 reject(failInfo)//当执行失败时可调用 //resolve或reject一旦调用则状态凝固,其参数会被传递给回调函数 }) .then(function(sucessInfo) { // success }, function(failInfo) { // failure })
reject
函数的参数通常是Error
对象的实例,表示抛出的错误;reject(new Error(this.failInfo));
resolve
函数的参数除了正常的值以外,还可能是另一个Promise
实例const p1 = new Promise(function (resolve, reject) { // ... }); const p2 = new Promise(function (resolve, reject) { // ... resolve(p1); }) .then(function(sucessInfo){ })
这时
p1
的状态就会传递给p2
,即p1
的状态决定了p2
的状态。
如果p1
的状态是pending
(正在进行),那么p2的回调函数就会等待p1
的状态改变;如果p1
的状态已经是resolved
或者rejected
,那么p2
的回调函数将会立刻执行。let p1=new Promise((resolve,reject)=>{ reject(2) }) let p2=new Promise((resolve)=>{ resolve(p1) }) .then((val)=>{console.log(val)}) .catch((val)=>{ console.log(new Error(val)) }) /*Error: 2 at <anonymous>:10:18*/ //可以发现即使外层的p2 resolve了,但是依然没有执行then,因为此时回调函数的状态取决于p1,p1 reject了,所以执行的回调函数是catch
调用
resolve
或reject
并不会终结Promise
的参数函数的执行(return才会
)。
resolve
和reject
函数会把参数包裹成一个新的Promise
立即
resolved
的Promise
是在本轮事件循环的末尾执行,总是晚于本轮循环的同步任务。new Promise((resolve, reject) => { resolve(1); console.log(2); }).then(r => { setTimeout(()=>{ console.log("aa") },1000) console.log(r); }); console.log(3) // 2 // 3 // 1 //aa
- 首先立即执行promise,发现resolve,把resolve放在微任务队列中,等到了本轮事件循环的末尾才能执行
- 执行打印,打印出2
- 状态未凝固,.then方法不执行,then方法被推入微任务队列中
- 执行打印,打印3
- 同步任务执行完了,所以把resolve从微任务队列推出到主线程执行
- promise状态凝固,微任务队列中的then方法回调函数达到执行条件,被推出微任务队列进入主线程执行队列执行
- 首先遇到settimeout函数,是一个宏任务(异步),被推入到宏任务执行队列,然后遇到同步任务,打印出resolve传递过来的1,这轮同步又执行完
- 如果1秒时间已经过去,则settimeout到达执行条件,被推入到主线程执行队列执行,打印出“aa"
- 总结:
调用顺序是同步任务-》微观任务-》宏观任务
promise是立即执行的
resolve()是用来表示promise的状态为fullfilled,相当于只是定义了一个有状态的Promise,但是并没有调用它;
promise调用then的前提是promise的状态已凝固;
-
规范写法
一般来说,调用resolve或reject以后,Promise 的使命就完成了,后继操作应该放到then方法里面,而不应该直接写在resolve或reject的后面。所以,最好在它们前面加上return语句,这样就不会有意外。new Promise((resolve, reject) => { return resolve(1); // 后面的语句不会执行 console.log(2); })
-
Promise.prototype.then()
//前面第一段代码 var promise = new Promise(function(resolve, reject) { resolve(sucessInfo)//当执行成功时可调用 reject(failInfo)//当执行失败时可调用 //resolve或reject一旦调用则状态凝固,其参数会被传递给回调函数 }) .then(function(sucessInfo) {//then方法的参数为两个回调函数,(可选),函数的参数sucessInfo为resolve函数的参数 // success }, function(failInfo) {//参数failInfo为reject函数的参数 // failure }) .then(.....)//链式写法
then
方法返回的是一个新的Promise
实例,因此可以采用链式写法,则第一个回调函数完成以后,会将返回结果作为参数,传入第二个回调函数。 -
Promise.prototype.catch()
.catch
相当于.then(null/undefined, rejection)
,用于指定发生错误时的回调函数,错误捕获会冒泡到外层,错误总是会被下一个catch
语句捕获。.catch
可以捕获前面的未被捕获的所有错误,catch()
方法返回的还是一个Promise
对象const p1 = new Promise((resolve, reject) => { setTimeout(() => resolve('成功'), 100) }) const p2 = new Promise((resolve, reject) => { setTimeout(() => reject(new Error('失败了')), 100) }) p1.then(function (post) { return p2 }) .then((resolve) => { //这里的代码不会执行,因为有错 console.log('第二个then执行') }) .catch((e) => { console.log(e, '=====第一个错误执行') }) .catch((e) => console.log(e, '=====第二个错误执行')) //这里的代码不会执行,因为没有未捕获的错误 .then((resolve) => { console.log('第三个then执行,接收到的返回值为:', resolve) }) /*Error: 失败了 at <anonymous>:6:35 "=====第一个错误执行"*/ //第三个then执行,接收到的返回值为: undefined
一般来说,使用
catch
方法捕获错误,不要在then()
方法里面定义Reject
状态的回调函数(即then
的第二个参数)。
Promise
内部的错误不会影响到Promise
外部的代码,通俗的说法就是“Promise
会吃掉错误”。 -
Promise.prototype.finally()
用于指定不管Promise
对象最后状态如何,都会执行的操作,finally
方法中的回调函数不接受任何参数
finally
方法总是会返回原来的值:// 不传参拿不到reject 的值 Promise.reject(3).then(() => {}, () => {}) //Promise {<fulfilled>: undefined} // 不传参也能拿到reject 的值 Promise.reject(3).finally(() => {}) //Promise {<rejected>: 3}
-
Promise.all()
用于将多个Promise
实例,包装成一个新的Promise
实例Promise.all([ Promise.resolve(1), Promise.resolve(2), ]) .then(([pro1, pro2]) => console.log(pro1, pro2)); //1 2
Promise.all()
中所有参数状态都变成fulfilled
,Promise.all()
才变成fulfilled
,所有实例参数的返回值组成一个数组,传递给Promise.all()
的回调函数Promise.all()
中有一个参数被rejected
,Promise.all()
的回调函数会接收第一个被reject
的实例的返回值- 参数实例自己定义了
catch
方法,那么它一旦被rejected
,会调用自己的catch
方法,没有错误后状态变为fulfilled
,并不会触发Promise.all()
的catch
方法。
-
Promise.race()
将多个Promise
实例,包装成一个新的Promise
实例
参数中有一个实例率先改变状态,Promise.race()
的状态就跟着改变。该实例的返回值就传递给回调函数const p = Promise.race([ promise1, new Promise(function (resolve, reject) { setTimeout(() => reject(new Error('request timeout')), 5000) }) ]); .then(console.log) .catch(console.error);
以上为5 秒之内
promise1
无法返回结果,变量p的状态就会变为rejected
,从而触发catch
方法指定的回调函数,反之在5秒内返回结果则触发then
方法的回调函数。 -
Promise.allSettled()
参数数组的所有Promise
对象都发生状态变更(不管是成功还是失败),Promise.allSettled()
的状态则变为成功(不会是失败),调用then方法的回调函数,打印传过来的参数,是一个对象数组,为固定模式//[{},{},...] // 异步操作成功时 {status: 'fulfilled', value: value} // 异步操作失败时 {status: 'rejected', reason: reason}
-
Promise.any()
与Promise.all()
相反:
有一个变成fulfilled状态—>变成fulfilled状态
所有参数实例都变成rejected状态—>变成rejected状态 -
Promise.resolve()
将现有对象转为 Promise 对象Promise.resolve('foo') // 等价于 new Promise(resolve => resolve('foo'))
参数类型情况详见 https://es6.ruanyifeng.com/#docs/promise#Promise-resolve
-
Promise.reject()
也会返回一个新的 Promise 实例,该实例的状态为rejected。
其参数,会原封不动地作为reject的理由,变成后续方法的参数。 -
Promise.try()
database.users.get()
返回一个Promise
对象,如果抛出异步错误,可以用catch
方法捕获;抛出同步错误(比如数据库连接错误,具体要看实现方法),这时你就不得不用try...catch
去捕获try { database.users.get({id: userId}) .then(...) .catch(...) } catch (e) { // ... }
这时就可以统一用
promise.catch()
捕获所有同步和异步的错误;Promise.try
就是模拟try
代码块Promise.try(() => database.users.get({id: userId})) .then(...) .catch(...)