• JavaScript中异步函数async、await


    async、await

    异步函数写法

    async关键字用于声明一个异步函数:

    • async是asynchronous单词的缩写,异步、非同步
    • sync是synchronous单词的缩写,同步、同时

    async异步函数和普通函数一样可以有很多中写法:

    // 1.最常用的方式
    async function foo1() {}
    // 2.表达式定义异步函数
    const foo2 = async function() {}
    // 3.箭头函数定义异步函数
    const foo3 = async () => {}
    // 4.类中定义异步函数
    class Person {
      async foo() {}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    异步函数返回值

    异步函数的内部代码执行过程和普通的函数是一致的,默认情况下也是会被同步执行。

    异步函数有返回值时,和普通函数会有区别(异步函数返回的是一个Promise):

    • 情况一:异步函数有普通的返回值时,异步函数的返回值相当于被包裹到Promise.resolve中

      async function foo() {
        return 321 // 相当于Promise.resolve(321)
      }
      
      foo().then(res => {
        console.log(res) // 321
      })
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
    • 情况二:如果我们的异步函数的返回值是Promise,状态由会由Promise决定

      const promise = new Promise((resolve, reject) => {
        setTimeout(() => {
          resolve(["aaa", "bbb", "ccc"])
        }, 3000)
      })
      
      async function foo() {
        return promise
      }
      
      foo().then(res => {
        console.log(res) // 延迟三秒打印['aaa', 'bbb', 'ccc']
      })
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
    • 情况三:如果我们的异步函数的返回值是一个对象并且实现了thenable,那么会由对象的then方法来决定

      async function foo() {
        return {
          then: function(resolve, reject) {
            setTimeout(() => {
            resolve("aaa")
            }, 3000)
          }
        }
      }
      
      foo().then(res => {
        console.log(res) // 延迟三秒打印 aaa
      })
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13

    异步函数的异常

    什么情况下, 异步函数是已拒绝 (rejected) 状态

    • 异步函数返回一个Promise主动调用reject会变成rejected状态

      async function foo() {
        return new Promise((resolve, reject) => {
          reject("err rejected")
        })
      }
      
      foo().then(res => {
        console.log("res:", res)
      }).catch(err => {
        console.log("err:", err) // err: err rejected
      })
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
    • 如果我们在async中抛出了异常,那么程序它并不会像普通函数一样报错,而是会作为Promise的reject来传递;

      async function foo() {
        // 抛出一个异常
        throw new Error("my async funtion error")
        return "aaa"
      }
      
      foo().then(res => {
        console.log("res:", res)
      }).catch(err => {
        // catch会捕获异常
        console.log("err:", err) // err: Error: my async funtion error
      })
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12

    await关键字

    async函数另外一个特殊之处就是可以在它内部使用await关键字,而普通函数中是不可以的。

    await关键字有什么特点呢?

    • 通常使用await是后面会跟上一个表达式,这个表达式会返回一个Promise

    • 那么await会等到Promise的状态变成fulfilled状态,之后继续执行异步函数

    • await后面表达的结果如果是fulfilled状态, 那么await会将Promise的resolve结果返回

      function bar() {
        console.log("bar函数")
        return new Promise((resolve, reject) => {
          setTimeout(() => {
            resolve(123)
          }, 3000)
        })
      }
      
      async function foo() {
        console.log("await关键字前面的代码")
      
        // await后续跟一个表达式, 表达式返回一个Promise时, 会等待Promise有结果后, 将resolve的结果返回, 继续执行后续代码
        const res = await bar()
        console.log(res)
      
        console.log("await关键字后面的代码")
      }
      
      foo()
      
      // 打印结果
      // 1. 直接打印
      // await关键字前面的代码
      
      // bar函数
      // 2. 等待三秒再打印
      // 123
      // await关键字后面的代码
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14
      • 15
      • 16
      • 17
      • 18
      • 19
      • 20
      • 21
      • 22
      • 23
      • 24
      • 25
      • 26
      • 27
      • 28
      • 29

    如果await后面是一个普通的值,那么会直接返回这个值;

    async function foo() {
      console.log("await关键字前面的代码")
    
      const res = await 123
      console.log(res)
    
      console.log("await关键字后面的代码")
    }
    
    foo()
    
    // 打印结果
    // await关键字前面的代码
    // 123
    // await关键字后面的代码
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    如果await后面的表达式,返回的Promiseed是reject的状态,那么相当于在异步函数中抛出了一个异常, 我们可以在catch方法中捕获, rejected状态后面的异步函数代码不会执行

    function bar() {
      console.log("bar函数")
      return new Promise((resolve, reject) => {
        setTimeout(() => {
          reject("err message")
        }, 3000)
      })
    }
    
    async function foo() {
      console.log("await关键字前面的代码")
    
      const res = await bar()
      console.log(res)
    
      console.log("await关键字后面的代码")
    }
    
    foo().catch(err => {
      console.log("err", err)
    })
    
    // 打印结果
    // 1.直接打印
    // await关键字前面的代码
    // bar函数
    
    // 2.等待三秒打印
    // err err message
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29

    如果await后面是一个thenable的对象,那么会根据对象的then方法调用来决定后续的值

    function bar() {
      console.log("bar函数")
      return {
        then: function(resolve, reject) {
          resolve("aaa")
        }
      }
    }
    
    async function foo() {
      console.log("await关键字前面的代码")
    
      const res = await bar()
      console.log(res)
    
      console.log("await关键字后面的代码")
    }
    
    foo()
    
    // 打印结果
    // await关键字前面的代码
    // bar函数
    // aaa
    // await关键字后面的代码
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25

    await处理异步请求

    上一篇中, 我们最终是使用生成器处理网络请求, 在学习了await后, 我们可以使用await处理异步请求, 再进一步的优化代码

    • 为了方便大家观看, 我将上一篇中的代码拿过来可以回顾一下

      // 封装模拟网络请求的函数
      function requestData(url) {
        return new Promise((resolve, reject) => {
          setTimeout(() => {
            resolve(url)
          }, 2000)
        })
      }
      
      // 生成器的处理方案
      function* getData() {
        const res1 = yield requestData("why")
        console.log("res1:", res1)
      
        const res2 = yield requestData(res1 + "kobe")
        console.log("res2:", res2)
      
        const res3 = yield requestData(res2 + "james")
        console.log("res3:", res3)
      }
      
      // 自动化执行生成器函数(了解)
      function execGenFn(genFn) {
        // 1.获取对应函数的generator
        const generator = genFn()
        // 2.定义一个递归函数
        function exec(res) {
          const result = generator.next(res)
          if (result.done) return
          result.value.then(res => {
            exec(res)
          })
        }
        // 3.执行递归函数
        exec()
      }
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14
      • 15
      • 16
      • 17
      • 18
      • 19
      • 20
      • 21
      • 22
      • 23
      • 24
      • 25
      • 26
      • 27
      • 28
      • 29
      • 30
      • 31
      • 32
      • 33
      • 34
      • 35
      • 36
    • 我们再使用async函数和await关键字重构上面代码

      function requestData(url) {
        return new Promise((resolve, reject) => {
          setTimeout(() => {
            resolve(url)
          }, 2000);
        })
      }
      
      async function getData() {
        const res1 = await requestData("aaa")
        console.log("res1:", res1) // aaa
      
        const res2 = await requestData(res1 + "bbb")
        console.log("res2:", res2) // aaabbb
        
        const res3 = await requestData(res2 + "ccc")
        console.log("res3:", res3) // aaabbbccc
      }
      
      // catch用于捕获异常
      getData().catch(err => {
        console.log("err:", err)
      })
      
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14
      • 15
      • 16
      • 17
      • 18
      • 19
      • 20
      • 21
      • 22
      • 23
      • 24
  • 相关阅读:
    【复杂句的逻辑练习题】that 省略
    用户画像的基本架构
    JavaWeb、终章案例
    宋浩高等数学笔记(一)函数与极限
    精力,而非时间是高效表现的基础
    mybatis学习(19):模糊查询#
    课堂教学观察方法与技术——阅读笔记
    Ubuntu20.04搭建RISC-V和qemu环境
    快手如何快速的进行引流,并且推荐几款我正在用的工具。
    c++暴力法
  • 原文地址:https://blog.csdn.net/m0_71485750/article/details/125497435