我想学懂async/await

109 阅读5分钟

概念

async函数是另一种处理异步编程的方法

async函数完全可以看作多个异步操作,包装成的一个 Promise 对象,而await命令就是内部then命令的语法糖。

通俗理解就是:

调用async 函数返回是promise实例后面可以接then()方法回调函数 ,async函数体内执行任务,遇到await命令会先停止执行,等到异步任务执行完成后再接着执行函数内后面的语句 。

基本用法

async函数多种写法

//函数声明
async function f() {}
//函数表达式
const foo = async function () {}
//对象方法
let obj = {
  async foo() {

  }
}
obj.foo().then()
//class 方法
class Store {
  constructor() {
  }

  async foo() {

  }
}
const store = new Store()
store.foo().then()
//箭头函数
const foo = async () => {}

async函数返回的是promise对象

也可以作为await命令后面的参数 看成async函数的嵌套使用

async function timeout() {
  return await new Promise((resolve, reject) => {
    setTimeout(() => {
      resolve(2)
    }, 1000)
  })
}

async function foo() {
  const _res = await timeout()
  return _res
}

foo().then(res => console.log(res))


函数内有return 值 状态会变fulfilled,then方法会接收回调函数
如果函数内抛出异常 状态会变rejected,catch方法会接收回调函数

async function f1() {
  return 'hello world'
}

f1().then(res => console.log(res))
//hello world
// f1() 状态变为fulfilled

async function f2() {
  throw new Error('error')
}

f2().catch(err => console.log(err))
//Error: error
//f2() 状态变为 rejected

async返回的promise状态变化

  • 取决于 内部的await命令后面的 promise对象状态变化以及 有无返回值\
  • 无return
  • return 普通值
  • return promise对象
  • 中间执行return
//async没有return
async function f7() {
  await new Promise((resolve, reject) => {
    resolve(123)
  })
}

f7().then(res => console.log(res))
//async 函数内没有return值,所有默认函数返回 undefined

//return promise
async function f3() {
  const res1 = await new Promise((resolve, reject) => {
    resolve(1)
  })
  console.log(res1)
  return new Promise((resolve, reject) => {
    resolve(2)
  })
  //也等同于
  // return await new Promise((resolve, reject)=>{resolve(2)})
}

f3().then(res => {
  console.log(res)
})
//return 一个promise对象,该promise执行完状态变为fulfilled,接后面的then方法
//输出: 1 2

//return 普通值
async function f4() {
  return 123
  //等同于
  // return await 123
}

f4().then(res => console.log(res))
//return普通值 等同于执行了 await 命令
//输出:123

//中间任务return
async function f5() {
  await new Promise((resolve, reject) => {
    resolve(1)
  })
  return 2
  await new Promise((resolve, reject) => {
    resolve(3)
  })
  console.log(4)
}

f5().then(res => console.log(res))
//中间执行了return 后面任务不在执行,f5()状态变为fulfilled
//输出:2

利用async await命令 特点演示休眠效果 让程序停顿指定的时间

function sleep(tm) {
  return new Promise((resolve, reject) => {
    setTimeout(resolve, tm)
  })
}

async function f6() {
  for (let i = 0; i < 5; i++) {
    console.log(i)
    await sleep(1000)
  }
}

f6()
//分析 先输出 0 然后遇到await命令 等待promise里的延迟任务执行完,再继续循环执行,以此类推
//每隔1秒输出一个数 0 1 2 3 4

中断执行

------以下几种情况await命令后面的任务会中断执行

  • await后面promise状态没有执行resolve方法 变为fulfilled
  • await语句后面的Promise对象变为rejected状态
  • await后面Promise异步任务抛出错误异常 特殊一个:await后面的promise体内有延时任务执行,后续的任务都要等待该异步任务状态变为fulfilled后再执行
//没有状态变化
const asyncFun1 = async function () {
  console.log(1)
  await new Promise((resolve, reject) => {
  }).then(res => console.log(2))
  console.log(3)
  await new Promise((resolve, reject) => {
    reject(4)
  }).catch(err => console.log(err))
  console.log(5)
}
asyncFun1()
//打印出:1
// 第一个await命令后面的promise没有状态变化,后续任务不在执行

//await命令后面promise 状态发生了变化
const asyncFun1 = async function () {
  console.log(1)
  const res1 = await new Promise((resolve, reject) => {
    resolve(2)
  })
  console.log(res1)
  console.log(3)
  await new Promise((resolve, reject) => {
    reject(4)
  }).catch()
  console.log(5)
}
asyncFun1()
//输出:1 2 3
//第二个await命令后面但promise实例的 catch函数必须是执行了回调状态才会变为 fulfilled,
所以没再执行后面的任务

const asyncFun1 = async function () {
  console.log(1)
  await new Promise((resolve, reject) => {
    resolve(2)
  }).then(res => console.log(res))
  console.log(3)
  await new Promise((resolve, reject) => {
    reject(4)
  }).catch(err => console.log(err))
  console.log(5)
}
asyncFun1()
//输出: 1 2 3 4 5
//两个promise 状态都变化为fulfilled 任务全部执行


//抛出异常
const asyncFun1 = async function () {
  console.log(1)
  await new Promise((resolve, reject) => {
    throw new Error('error')
  })
  console.log(3)
}
asyncFun1().then(null, (err) => {
  console.log(err)
})
//await后面promise抛出了异常会被当作变为了rejected状态,后面的任务不再执行
//输出: 1  Error: error

//有延时的宏任务执行
const asyncFun1 = async function () {
  console.log(1)
  await new Promise((resolve, reject) => {
    setTimeout(() => {
      resolve(2)
    }, 2000)
  }).then(res => console.log(res))
  console.log(3)
  await new Promise((resolve, reject) => {
    reject(4)
  }).catch(err => console.log(err))
  console.log(5)
}
asyncFun1()
//第一个promise里有延迟任务,等待延迟任务执行完后 再陆续执行后面的任务
//输出:1 2秒后 输出 2 3 4 5

错误处理

有时候希望即使前面的任务执行出错,后面的任务也还能继续执行,用 try catch 包裹会出错的任务

async function f7() {
  try {
    await Promise.reject('error')
    await new Promise((resolve, reject) => {
      throw new Error('error')
    })
  } catch (e) {

  }
  return await Promise.resolve('继续执行')
}

f7().then(res => console.log(res))
//输出:继续执行

案例:使用 try catch 实现 尝试重复多次

const test = (i) => {
  let num = Math.floor(Math.random() * 10)
  return new Promise((resolve, reject) => {
    if (num % 2) {
      console.log('resolve--', i)
      resolve('fulfilled')
    } else {
      console.log('reject--', i)
      reject('rejected')
    }
  })
}

async function f8() {
  let i;
  for (i = 0; i < 5; i++) {
    try {
      await test(i)
      break
    } catch (e) {
      console.log(e, '----')
    }
  }
  console.log(i)
}

f8()
/*reject-- 0
 reject-- 1
 reject-- 2
 resolve-- 3
 3*/

//await后面的如果成功,会执行break中断循环,如果失败被catch语句捕捉,继续循环

继发与并发

如果两个异步的操作互相不依赖不是继发关系,可以处理为并发执行

async function f9() {
  // 写法一
  let [foo, bar] = await Promise.all([getFoo(), getBar()]);

// 写法二
  let fooPromise = getFoo();
  let barPromise = getBar();
  let foo = await fooPromise;
  let bar = await barPromise;
}

自己能力不足,还是把阮一峰老师的例子记录上,觉得以后会有这种场景出现需要使用到,
遇到不会的就先跟着手写一遍锻炼自己的手感,写着写着可能发现自己还是不会,不会就是不会。
DOM元素上部署多个动画操作,前一个结束后再执行下一个动画,如果有一个动画出错了,返回上一个动画。

//Promise写法
function f10(elem, animations) {

  //用变量保存上一个动画返回值
  let ret = null;
  //建一个空的promise
  let p = Promise.resolve()
  //循环 then方法调用动画
  for (let anim of animations) {
    p = p.then((val) => {
      ret = val;
      return anim(elem)
    })
  }
  //如果前一个动画报错p会走到catch方法,后面再接then方法返回上一个动画
  return p.catch(err => {
    console.log(err)
    // 抛出错误,继续执行
  }).then(() => {
    return ret;
  })

}
//async await写法
async function f11(elem, animations) {
  let ret;
  try {
    for (let anim of animations) {
      ret = await anim(elem)
    }
  } catch (e) {
    //抛出错误,继续执行
  }
  return ret;
}

案例:

一组异步操作需要按照顺序完成,比如依次读取异步任务,不管其中哪个先执行结束,最后
还是要按照读取的顺序输出结果

//promise写法
const p1 = new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve('p1')
  }, 3000)
})
const p2 = new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve('p2')
  }, 1000)
})
const p3 = new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve('p3')
  }, 2000)
})

let promises = [p1, p2, p3]

function f12(asyncTaskList) {
  //先并发处理所有异步任务
  const taskPromises = asyncTaskList.map((asyncTask) => {
    return asyncTask.then(res => res)
  })
  console.log(taskPromises, '--taskPromises') //异步任务执行后的有返回值的 promise
  //按次序输出
  taskPromises.reduce((chain, taskPromise) => {
    return chain.then(() => taskPromise).then(res => {
      console.log(res)
    })
  }, Promise.resolve())
}

f12(promises)
//async函数实现
const p1 = new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve('p1')
  }, 3000)
})
const p2 = new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve('p2')
  }, 1000)
})
const p3 = new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve('p3')
  }, 2000)
})

let promises = [p1, p2, p3]
async function f13(asyncTaskList) {
  //并发处理任务
  const taskPromises = asyncTaskList.map(async asyncTask => {
    return await asyncTask
  })
  //按次序输出
  for (let task of taskPromises) {
      console.log(await task)
  }
}
f13(promises)
//过程的推理演算
async function f14() {
  const res = await new Promise((resolve, reject)=>{resolve(123)})
  return res
}
// f14().then(res=>console.log(res))
//123

async function f15() {
  console.log(await f14())
  //f14()是状态为fulfilled且有返回值的promise实例
  //此时在f15() 前面又使用await命令 
  //等同于
 /* const res = await f14()
  console.log(res)*/
}
f15()
//123