async、await语法详解

123 阅读5分钟

一、异步处理

1.1 处理一 - 回调地狱

  • 需求:
    1. 发送第一次网络请求
    2. 发送第二次网络请求, 依赖第一次请求的结果
    3. 发送第三次网络请求, 依赖第二次请求的结果
// 封装请求的方法: url -> promise(result)
function requestData(url) {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      resolve(url)
    }, 2000)
  })
}

// 方式一: 层层嵌套(回调地狱 callback hell)
function getData() {
  // 1.第一次请求
  requestData("aaa").then(res1 => {
    console.log("第一次结果:", res1)

    // 2.第二次请求
    requestData(res1 + "bbb").then(res2 => {
      console.log("第二次结果:", res2)

      // 3.第三次请求
      requestData(res2 + "ccc").then(res3 => {
        console.log("第三次结果:", res3)
      })
    })
  })
}

1.2 处理二 - Promise链式

function getData() {
  requestData("aaa").then(res1 => {
    console.log("第一次结果:", res1)
    return requestData(res1 + "bbb")
  }).then(res2 => {
    console.log("第二次结果:", res2)
    return requestData(res2 + "ccc")
  }).then(res3 => {
    console.log("第三次结果:", res3)
  })
}

1.3 处理三 - generator+yield

function* getData() {
  const res1 = yield requestData("aaa")
  console.log("res1:", res1)

  const res2 = yield requestData(res1 + "bbb")
  console.log("res2:", res2)

  const res3 = yield requestData(res2 + "ccc")
  console.log("res3:", res3)
}

const generator = getData()
generator.next().value.then(res1 => {
  generator.next(res1).value.then(res2 => {
    generator.next(res2).value.then(res3 => {
      generator.next(res3)
    })
  })
})

1.4 处理四 - await/async

async function getData() {
  const res1 = await requestData("aaa")
  console.log("res1:", res1)

  const res2 = await requestData(res1 + "bbb")
  console.log("res2:", res2)

  const res3 = await requestData(res2 + "ccc")
  console.log("res3:", res3)
}

1.5 封装自动执行生成器函数工具

// 封装请求的方法: url -> promise(result)
function requestData(url) {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      resolve(url)
    }, 2000)
  })
}

// 生成器的处理方案
function* getData() {
  const res1 = yield requestData("aaa")
  console.log("res1:", res1)

  const res2 = yield requestData(res1 + "bbb")
  console.log("res2:", res2)

  const res3 = yield requestData(res2 + "ccc")
  console.log("res3:", res3)

  const res4 = yield requestData(res3 + "ddd")
  console.log("res4:", res4)

  const res5 = yield requestData(res4 + "eee")
  console.log("res5:", res5)
}

// const generator = getData()
// generator.next().value.then(res1 => {
//   generator.next(res1).value.then(res2 => {
//     generator.next(res2).value.then(res3 => {
//       generator.next(res3).value.then(res4 => {
//         generator.next(res4)
//       })
//     })
//   })
// })

// 自动化执行生成器函数
function execGenFn(genFn) {
  // 1.获取对应函数的generator
  const generator = genFn()
  // 2.定义一个递归函数
  function exec(res) {
    // result -> { done: true/false, value: 值/undefined }
    const result = generator.next(res)
    if (result.done) return
    result.value.then(res => {
      exec(res)
    })
  }
  // 3.执行递归函数
  exec()
}

execGenFn(getData)

二、async/await

2.1 异步函数async

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

    • async是asynchronous单词的缩写,异步、非同步
    • sync是synchronous单词的缩写,同步、同时
    // 普通函数
    // function foo() {}
    // const bar = function() {}
    // const baz = () => {}
    
    // 生成器函数
    // function* foo() {}
    
    // 异步函数
    async function foo() {
      console.log("foo function1")
      console.log("foo function2")
      console.log("foo function3")
    }
    foo()
    
    // const bar = async function() {}
    // const baz = async () => {}
    // class Person {
    //   async running() {}
    // }
    

2.2 异步函数返回值

在 JavaScript 中,async 函数总是返回一个 Promise 对象。即使函数内部返回的是一个数组或其他值,它最终会被包裹在一个 Promise 对象中。

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

  • 异步函数有返回值时,和普通函数会有区别:

    • 如果异步函数的返回值是普通值,相当于被包裹到Promise.resolve中
    • 如果异步函数的返回值是Promise,状态由会由Promise决定
    • 如果异步函数的返回值是一个对象并且实现了thenable,那么会由对象的then方法来决定
    async function foo2() {
      // 1.返回一个普通的值
      // -> Promise.resolve(321)
      return ["abc", "cba", "nba"]
    
      // 2.返回一个Promise
      // return new Promise((resolve, reject) => {
      //   setTimeout(() => {
      //     resolve("aaa")
      //   }, 3000)
      // })
    
      // 3.返回一个thenable对象
      // return {
      //   then: function(resolve, reject) {
      //     resolve("bbb")
      //   }
      // }
    }
    
    foo2().then(res => {
      console.log("res:", res)
    })
    
  • 注意:如果在async中抛出了异常,程序并不会像普通函数一样报错,而是会作为Promise的reject来传递

    // 如果异步函数中有抛出异常(产生了错误), 这个异常不会立即被浏览器处理
    // 进行如下处理: Promise.reject(error)
    async function foo() {
      console.log("---------1")
      console.log("---------2")
      // "abc".filter()
      throw new Error("async function error")
      console.log("---------3")
    
      // return new Promise((resolve, reject) => {
      //   reject("err rejected")
      // })
    
      return 123
    }
    
    // promise -> pending -> fulfilled/rejected
    foo().then(res => {
      console.log("res:", res)
    }).catch(err => {
      console.log("err:", err)
      console.log("继续执行其他的逻辑代码")
    })
    

2.3 async中使用await

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

  • await关键字的特点

    • 通常使用await是后面会跟上一个表达式,这个表达式会返回一个Promise,那么await会等到Promise的状态变成fulfilled状态,之后继续执行异步函数;
    function bar() {
      console.log("bar function")
      return new Promise(resolve => {
        setTimeout(() => {
          resolve(123)
        }, 100000)
      })
    }
    
    async function foo() {
      console.log("-------")
      // await后续返回一个Promise, 那么会等待Promise有结果之后, 才会继续执行后续的代码
      const res1 = await bar()
      console.log("await后面的代码:", res1)
      const res2 = await bar()
      console.log("await后面的代码:", res2)
    
      console.log("+++++++")
    }
    
    foo()
    
  • await后面跟不同的值的情况

    • 如果await后面是一个普通的值,那么会直接返回这个值
    • 如果await后面是一个thenable的对象,那么会根据对象的then方法调用来决定后续的值
    • 如果await是一个promise对象,会阻断后续的代码,等待状态变为fullfilled,才获取结果并执行后续代码;如果返回的promise是reject状态,会将这个reject结果直接作为Promise的reject值
    (async function () {
      const p = Promise.reject("err"); // rejected状态
      try {
        const res = await p;
      } catch (e) {
        console.log(e); // err  // try...catch 相当于 Promise 的 catch
      }
    })();
    
    (async function () {
      const p = Promise.reject("err"); // rejected状态
      const res = await p; // await 相当于promise的then
      console.log("res", res); // 不会执行
    })();
    
  • await后不仅可以跟一个普通函数返回一个Promise,也可以跟一个异步函数

    // 1.定义一些其他的异步函数
    function requestData(url) {
      console.log("request data")
      return new Promise((resolve) => {
        setTimeout(() => {
          resolve(url)
        }, 3000)
      })
    }
    
    async function test() {
      console.log("test function")
      return "test"
    }
    
    async function bar() {
      console.log("bar function")
    
      return new Promise((resolve) => {
        setTimeout(() => {
          resolve("bar")
        }, 2000);
      })
    }
    
    async function demo() {
      console.log("demo function")
      return {
        then: function(resolve) {
          resolve("demo")
        }
      }
    }
    
    // 2.调用的入口async函数
    async function foo() {
      console.log("foo function")
    
      const res1 = await requestData("abc")
      console.log("res1:", res1)
    
      const res2 = await test()
      console.log("res2:", res2)
    
      const res3 = await bar()
      console.log("res3:", res3)
    
      const res4 = await demo()
      console.log("res4:", res4)
    }
    
    foo()