• 10.Async异步函数


    1.Async函数介绍


    asnyc函数实际上是Generator+Promise的语法糖

    const fs = require('fs')
    
    function readFile(fileName) {
      return new Promise(function (resolve, reject) {
        fs.readFile(fileName, function(error, data) {
          if (error) return reject(error)
          resolve(data)
        })
      })
    }
    
    function* gen() {
      const f1 = yield readFile('/etc/fstab')
      const f2 = yield readFile('/etc/shells')
      console.log(f1.toString())
      console.log(f2.toString())
    }
    let f = gen()
    f.next()
    f.next()
    f.next()
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    上述代码修改成async函数

    async function asyncReadFile (){
      const f1 = await  readFile('/etc/fstab')
      const f2 = await  readFile('/etc/shells')
      console.log(f1.toString())
      console.log(f2.toString())
    }
    asyncReadFile()
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    2.基本用法


    async函数返回一个Promise对象,可以使用then方法进行回调。当函数执行遇到await就会先返回,等异步操作完成后,在接着执行函数体后面的语句

    async function getStockPriceByName(name) {
      const symbol = await getStockSymbol(name)
      const stockPrice = await getStockPrice(symbol)
      return stockPrice
    }
    
    getStockPriceByName('goog').then(function (result) {
      console.log(result)
    })
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    function timeout(ms) {
      return new Promise((resolve) => {
        setTimeout(resolve, ms) // ms秒之后resolve
      });
    }
    
    async function asyncPrint(value, ms) {
      await timeout(ms)
      console.log(value)
    }
    
    asyncPrint('hello world', 50) // 在50ms后才会输出hello world
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    3.async声明方式


    // 函数声明
    async function fn() {}
    
    // 函数表达式
    const fn = async function () {}
    
    // 箭头函数
    const fn = async () => {}
    
    // 对象方法
    let obj = { async fn() {} }
    
    // Class的方法
    class Storage {
      constructor() { ... }
      async fn() { ... }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    4.async本质上是Promise


    async函数结束return的参数会封装成为一个Promise,状态为fulfilled,因为函数本身就是一个Promise

    async function fn(){
      return 'data'
    }
    fn().then(res => console.log(res)) // data
    
    • 1
    • 2
    • 3
    • 4

    同理,throw抛出错误会返回一个Promise,状态为rejected

    async function fn(){
      throw new Error('出错了')
    }
    fn().then{
      res => console.log('resolve: ' + res),
      err => console.log('reject: ' + err)
    }
    // reject Error: 出错了
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    返回的Promise对象,必须等到await命令后面的Promise对象执行完,才会发生状态改变,意味着必须等到async函数异步操作执行完才会执行then(看有关Generator函数和Promise结合模拟实现async/await会更清楚这个过程)

    async function getTitle(url) {
      let res = await fetch(url)
      let html = await res.text()
      return html.match(/<title>([\s\S]+)<\/title>/i)[1]
    }
    getTitle('https://www.baidu.com').then(res => console.log(res))
    // 抓取网页、取出文本、匹配页面标题。只有这三个操作全部完成,才会执行then方法里面的console.log
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    5.await命令后面若是thenable对象,会将其视为Promise处理,调用其then方法并接收其resolve函数的参数

    class Sleep {
      constructor(timeout) {
        this.timeout = timeout
      }
      then(resolve, reject) {
        const startTime = Date.now();
        setTimeout(
          () => resolve(Date.now() - startTime),
          this.timeout
        )
      }
    }
    (async () => {
      const sleepTime = await new Sleep(3000)
      console.log(sleepTime);
    })()
    // 3000
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    6.await命令后面的Promise对象处于reject状态时,会立即中断执行,并抛错传入catch回调函数

    async function f() {
      await Promise.reject('出错了');
      await Promise.resolve('hello world'); // 不会执行
    }
    f()
    .then(v => console.log(v)) // 不执行
    .catch(e => console.log(e)) // 出错了
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    若不希望中断,那么就要把其放入try...catch

    async function f() {
      try{
        await Promise.reject('出错了');
      }catch(e){
        console.log(e) // 出错了
      }
      await Promise.resolve('hello world'); // 继续执行
    }
    f()
    .then(v => console.log(v)) // hello world
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    或者及时在await后面的Promise对象调用catch方法处理错误

    async function f() {
      await Promise.reject('出错了').catch(e => console.log(e))
      return await Promise.resolve('hello world'); // 继续执行
    }
    f().then(v => console.log(v))
    // 出错了
    // hello world
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    7.良好实践

    如果await后面的异步操作出错,那么等同于async函数返回的Promise 对象被reject

    async function f() {
      await new Promise(function (resolve, reject) {
        throw new Error('出错了')
      })
    }
    
    f()
    .then(v => console.log(v))
    .catch(e => console.log(e)) // Error:出错了
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    防止出错方法,将其放入try...catch

    async function f() {
      try {
        await new Promise(function (resolve, reject) {
          throw new Error('出错了')
        })
      } catch(e) {// ...在这里处理错误}
      return await('hello world')
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    async function main() {
      try {
        const val1 = await firstStep()
        const val2 = await secondStep(val1)
        const val3 = await thirdStep(val1, val2)
        return val3
      }
      catch (e) {
        console.error(e)
      }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    多次尝试获取数据

    const superagent = require('superagent')
    const NUM_RETRIES = 3
    
    async function test() {
      let i
      for (i = 0; i < NUM_RETRIES; ++i) {
        try {
          await superagent.get('http://google.com/this-throws-an-error')
          break
        } catch(err) {}
      }
      console.log(i)
    }
    test() // 3
    // 证明请求都是无效的,因为只要成功,就会立刻break跳出循环
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    将继发改为并行触发(很重要)

    async function fn(){
      let foo = await getFoo()
      let bar = await getBar()
      // 这里bar要等foo获得数据后才会执行,是继发的关系,
      // 但是,这样很费事,两个应该是并行触发的才对
    
      // 改进方法
      let [foo, bar] = await Promise.all([getFoo(), getBar()])
      // 或者
      let fooPromise = getFoo()
      let barPromise = getBar()
      let foo = await fooPromise
      let bar = await barPromise
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
  • 相关阅读:
    OpenStack常用命令
    iText7高级教程之构建基础块——6.创建动作(Action)、目标(destinations)和书签(bookmarks)
    Mac配置host
    TI/德州仪器 LMV331M7 模拟比较器
    python协程asyncio:async与await
    Java 8 新特性 (#Lambda表达式 #函数式(Functional)接口 #方法引用与构造器引用 #Stream API #Optional类)
    关于HM NISEDIT在新版系统下编译并运行提示权限不足问题的解决方案
    神经网络的发展
    Spring boot Actuator监控管理的快速入门和实战
    scrapy简单实现一个项目
  • 原文地址:https://blog.csdn.net/Xiaoyc7/article/details/125536297