来来来,学了不吃亏,async await 了解一哈,撒

435 阅读4分钟

这是我参与8月更文挑战的第4天,活动详情查看:8月更文挑战

话不多说,直接开干。。。

什么是 async 函数

  • 很简单,像下面👇这样,普通函数前面加个 async 关键字,就是 async函数了。
async function fn() {
    // 你的某些骚操作
    await console.log('随便写什么东西')
}

怎么样,看到这是不是已经准备默默的开始鄙视😒了,就这。。。

看官别急,咱们继续。。。

  • async 函数就是 Generator 函数的语法糖。
  • 同样的 async 也可以用来解决回调地狱的问题,而且它更像是同步的写法。
  • 其具有以下的特点:
    1. 内置执行器(相对于Generator函数来说)
    2. 更好的语义
      • async表示函数里有异步操作,await表示紧跟在后面的表达式需要等待结果。
    3. 更广的适用性
      • async函数的await命令后面,可以是 Promise 对象和原始类型的值(数值、字符串和布尔值,但这时会自动转成立即 resolved 的 Promise 对象)。
    4. 返回值是Promise
      • async函数的返回值是Promise对象。这也就意味着你可以用then方法指定接下来的操作。

用法

  • async函数返回一个Promise对象,可以使用then方法添加回调函数。
  • 当函数执行的时候,一旦遇到 await 就会先返回等到异步操作完成,再接着执行函数体内后面的语句。 看到这,相信你已经知道了一些关于async函数的基本的东西。俗话说的好,学了就是拿来用的,开干。。。
// 函数声明
async function foo() {}

// 函数表达式
const foo = async function () {};

// 对象的方法
let obj = { async foo() {} };
obj.foo().then(...)

// Class 的方法
class Storage {
  constructor() {
    this.cachePromise = caches.open('avatars');
  }

  async getAvatar(name) {
    const cache = await this.cachePromise;
    return cache.match(`/avatars/${name}.jpg`);
  }
}

const storage = new Storage();
storage.getAvatar('jake').then(…);

// 箭头函数
const foo = async () => {};

返回promise对象

  • async函数返回一个 Promise 对象。
  • async函数内部return语句返回的值,会成为then方法回调函数的参数。
  • async函数内部抛出错误,会导致返回的 Promise 对象变为reject状态。抛出的错误对象会被catch方法回调函数接收到。

Promise对象的状态变化

  • async函数返回的 Promise 对象,必须等到内部所有await命令后面的 Promise 对象执行完,才会发生状态改变,除非遇到return语句或者抛出错误。
  • 也就是说:只有async函数内部的异步操作执行完,才会执行then方法指定的回调函数。 看个🌰:
async function getTitle(url) {
  let response = await fetch(url);
  let html = await response.text();
  return html.match(/<title>([\s\S]+)</title>/i)[1];
}
getTitle('https://tc39.github.io/ecma262/').then(console.log)
// "ECMAScript 2017 Language Specification"

上述代码中:只有等 async函数getTitle体内的三个操作(抓取网页、取出文本、匹配标题)全部完成,才会执行then方法里边的console.log

await命令

  • 正常情况下:

    • await命令后面是一个 Promise 对象,返回该对象的结果。
    • 如果不是 Promise 对象,就直接返回对应的值。
  • 另一种情况:

    • await命令后面是一个 thenable对象(即定义了then方法的对象),那么await会将其等同于Promise对象
  • 任何一个await语句后面的Promise对象变为reject状态,那么整个async函数都会中断执行。

async function f() {
  await Promise.reject('出错了');
  await Promise.resolve('hello world'); // 不会执行
}

此时,如果我们依然需用后续的await能够正常执行,就需要将第一个await放在try。。。catch结构中。

async function f() {
  try {
      await Promise.reject('出错了');
  } catch(e) {
  
  }
  return await Promise.resolve('hello world'); 
}

f().then(val => console.log(val))

那么,还有别的处理方式吗?那必须有哇。 可以在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

错误处理

  • 如果await后面的异步操作出错,那么等同于async函数返回的Promise对象被reject。
  • 此时,使用try...catch结构包裹 await语句。

注意的点

  • 如果多个await命令后面的异步操作,不存在依赖关系,最好让它们同时出发。如下:
// 写法一
let [foo, bar] = await Promise.all([getFoo(), getBar()]);

// 写法二
let fooPromise = getFoo();
let barPromise = getBar();
let foo = await fooPromise;
let bar = await barPromise;
  • await 只能用在 async函数中。

  • async 函数可以保留运行堆栈。

本文参考:es6入门

相关面试考题

async function async1(){
    console.log('async1 start')
    await async2()
    console.log('async1 end')
}
async function async2(){
    console.log('async2')
}
console.log('script start')
setTimeout(function(){
    console.log('setTimeout')
},0)  
async1();
new Promise(function(resolve){
    console.log('promise1')
    resolve();
}).then(function(){
    console.log('promise2')
})
console.log('script end')

更全的可以移步这里:2021前端面试知识点总结之js篇

结束

别滑啦,到底啦,没啦。。。