Visual LabINTERACTIVE LEARNING
JavaScript 内核第 4 / 7 章

Promise 与 async / await

从 Promise 状态流转进入现代异步函数。

this原型异步
进阶预计 65 分钟查看源文 ↗

异步编程

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状态,才会继续往下执行