JavaScript中异步函数async、await

导读:本篇文章讲解 JavaScript中异步函数async、await,希望对大家有帮助,欢迎收藏,转发!站点地址:www.bmabk.com

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() {}
}

异步函数返回值

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

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

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

    async function foo() {
      return 321 // 相当于Promise.resolve(321)
    }
    
    foo().then(res => {
      console.log(res) // 321
    })
    
  • 情况二:如果我们的异步函数的返回值是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']
    })
    
  • 情况三:如果我们的异步函数的返回值是一个对象并且实现了thenable,那么会由对象的then方法来决定

    async function foo() {
      return {
        then: function(resolve, reject) {
          setTimeout(() => {
          resolve("aaa")
          }, 3000)
        }
      }
    }
    
    foo().then(res => {
      console.log(res) // 延迟三秒打印 aaa
    })
    

异步函数的异常

什么情况下, 异步函数是已拒绝 (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
    })
    
  • 如果我们在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
    })
    

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关键字后面的代码
    

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

async function foo() {
  console.log("await关键字前面的代码")

  const res = await 123
  console.log(res)

  console.log("await关键字后面的代码")
}

foo()

// 打印结果
// await关键字前面的代码
// 123
// await关键字后面的代码

如果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

如果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关键字后面的代码

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()
    }
    
  • 我们再使用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)
    })
    
    

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/120126.html

(0)
seven_的头像seven_bm

相关推荐

发表回复

登录后才能评论
极客之音——专业性很强的中文编程技术网站,欢迎收藏到浏览器,订阅我们!