JavaScript 内核第 4 / 7 章
Promise 与 async / await
从 Promise 状态流转进入现代异步函数。
异步编程
Promise
Promise的使用
构造函数的参数 excutor
⚽ 提示
本身也是一个函数,会自动传入2个参数,函数体会在new promise时被立即执行
const excutor = (resolve, reject) => {}
const promise = new Promise(excutor)
resolve的值的类型
- 普通值,直接返回
- 新的Promise,以新的promise的值为值
- thenable的对象,即实现了then方法的对象,then方法会被自动调用,且传入resolve,reject参数,resolve的值就是then的结果,reject就是catch结果
const value1 = 'aaa'
const valueObj = {name:'aaa'}
const promiseRes = (resolve,reject)=>{
setTimeout(() => {
resolve('promiseRes')
}, 2);
}
const promiseErr = (resolve,reject)=>{
setTimeout(() => {
reject('promiseErr')
}, 4);
}
const thenable = {
name: 'thenable',
then: (resolve,reject) =>{
resolve('thenable')
}
}
function foo(){
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve(value1) // res :>> aaa
// resolve(valueObj) // res :>> { name: 'aaa' }
// resolve(new Promise(promiseRes)) // res :>> promiseRes
// resolve(new Promise(promiseErr)) // err :>> promiseErr
// resolve(thenable) //res :>> thenable
}, 4);
})
}
foo().then(res=>{
console.log('res :>> ', res);
}).catch(err=>{
console.log('err :>> ', err);
})
then方法的细节
- 可以传入两个参数(回调函数),分别是成果和失败的回调
- 一个promise可以使用多个then,每个then都会被回调
- then后面可以链式
- then/catch里面再返回一个值,会产生一个新的promise,并把返回值作为resolve的值,链式操作时会作为下一个then的结果,throw new Error() 会被下一个最近的catch捕获
function foo(m){
return new Promise((resolve, reject) => {
setTimeout(() => {
if(m>3){
resolve('succ')
}else {
reject('err')
}
}, 4);
})
}
const promise = foo(4)
// 1. then 可以传入2个参数
promise.then(res=>{
console.log('res1 :>> ', res);
},err=>{
console.log('err :>> ', err);
})
//2. 同一个promise,then可以监听多次,都会被回调执行
promise.then(res=>{
console.log('res1 :>> ', res);
})
promise.then(res=>{
console.log('res2 :>> ', res);
})
promise.then(res=>{
console.log('res3 :>> ', res);
})
// 3. then后面可以链式操作
promise.then(res=>{
console.log('res1 :>> ', res);
}).catch(err=>{
console.log('err :>> ', err);
})
// 4. then/catch里面再返回一个值,会产生一个新的promise,并把返回值作为resolve的值,
// 链式操作时会作为下一个then的结果,
// throw new Error() 会被下一个最近的catch捕获
promise.then(res=>{
console.log('res1 :>> ', res);
throw new Error('bad')
}).catch(err=>{
console.log('error1 :>> ', String(err));
throw new Error('bad2')
}).catch(err=>{
console.log('error2 :>> ', String(err));
return 'from error'
}).then(res=>{
console.log('res2 :>> ', res);
})
catch方法的细节
- 一个promise可以单独多次使用catch,都会被独立回调
- then后面没有捕获错误,而是如果出现错误则会报错,即使已经单独catch了
function foo(m){
return new Promise((resolve, reject) => {
setTimeout(() => {
if(m>3){
resolve('succ')
}else {
reject('err')
}
}, 4);
})
}
const promise = foo(3)
//2. 同一个promise,catch可以监听多次,都会被回调执行
promise.catch(err=>{
console.log('err1 :>> ', err);
})
promise.catch(err=>{
console.log('err2 :>> ', err);
})
promise.catch(err=>{
console.log('err3 :>> ', err);
})
// 2. then后面如果没有catch,即使promise已经在上面直接catch了,这里的then仍然会报错
promise.then(res=>{
console.log('res1 :>> ', res);
})
finally回调
ES9(ES2018)新增的特性
无用论fullfilled还是rejected,都会回调这个函数
promise.then(res=>{}).catch(()=>{}).finally(()=>{
//最终一定会执行的方法
})
Promise执行时机
function foo (m){
return new Promise((resolve, reject) => {
console.log('immedi',1)
setTimeout(() => {
console.log('setTimeout0',2)
}, 0);
setTimeout(() => {
if(m>1){
console.log('resolve', 3)
resolve(1)
console.log('after resolve', 4)
}else {
console.log('reject', 5)
reject('错误')
console.log('after reject', 6)
}
console.log('setTimeout3',7)
}, 3);
})
}
foo(4).then(res=>{
console.log('then', 8)
}).catch(err=>{
console.log('catch', 9)
})
console.log('outside',10)
// 执行结果
// immedi 1
// outside 10
// setTimeout0 2
// resolve 3
// after resolve 4
// setTimeout3 7
// then 8
Promise类方法
Promise.resolve /reject
允许直接使用,不需要new一个实例,返回一个promise
Promise.all
全部的promise执行完成
- 返回一个新的promise,状态由所有的promise共同决定,当所有promisefullfilled时,最终状态为fullfilled,在then中回调所有的执行结果的数组
- 有一个promise为reject,则最终状态为reject,并返回第一个reject结果,可以catch到第一个error
Promise.allSettled
ES11 (ES2012) 新增
全部promise都执行,这个promise的状态一定是fullfilled,结果在then中返回一个数组,包含了state
const p1 = new Promise((resolve,reject)=>{
reject('p1')
})
const p2 = new Promise((resolve,reject)=>{
reject('p2')
})
const p3 = new Promise((resolve,reject)=>{
resolve('p3')
})
Promise.allSettled([p1,p2,p3]).then(res=>{
console.log('res', res)
// res [
//{ status: 'rejected', reason: 'p1' },
//{ status: 'rejected', reason: 'p2' },
//{ status: 'fulfilled', value: 'p3' }
//]
})
Promise.race
返回的promise状态由最快的一个promise决定
const p1 = new Promise((resolve,reject)=>{
setTimeout(() => {
reject('p1')
}, 6);
})
const p2 = new Promise((resolve,reject)=>{
setTimeout(() => {
reject('p2')
}, 1);
})
const p3 = new Promise((resolve,reject)=>{
setTimeout(() => {
resolve('p3')
}, 2);
})
Promise.race([p1,p2,p3]).then(res=>{
console.log('res', res)
}).catch(err=>{
console.log('err', err) // err p2
})
Promise.any
返回最快的一个fullfilled,如果没有fullfilled,则最终会是reject状态
const p1 = new Promise((resolve,reject)=>{
setTimeout(() => {
reject('p1')
}, 6);
})
const p2 = new Promise((resolve,reject)=>{
setTimeout(() => {
reject('p2')
}, 1);
})
const p3 = new Promise((resolve,reject)=>{
setTimeout(() => {
resolve('p3')
}, 2);
})
Promise.any([p1,p2,p3]).then(res=>{
console.log('res', res) // res p3
}).catch(err=>{
console.log('err', err)
})
async/await
🏕️ 提示
实际上是生成器函数的语法糖,并且做了一些优化 生成器函数返回一个迭代器,调用next方法返回每个yield后面的promise,再promise的then中再次调用next并把结果当成参数传递进去
async用于定义一个异步函数
await只能在异步函数中使用 (ES8),以及在模块顶层使用(ES13)
async function foo(){
await promise
}
- 会等await后面的promise有fullfilled状态,才会继续往下执行