ES6完全学习笔记 之 Async / await

  • 原创
  • 作者:程序员三丰
  • 发布时间:2022-06-07 18:58
  • 浏览量:492
前端开发使用异步操作太频繁了,继之前学习了Promise之后,又抽时间系统的学习了的async函数和await命令的原理和用法,学完之后受益匪浅,真是大开眼界,JS在异步操作的强大完全刷新了之前的认知,也让我明白了之前很多难以实现应用场景、难以解决技术难题的最优解,简直是醍醐灌顶,茅塞顿开,也让我在前端路上继续走下去的信念更加坚定!

Async 的含义

含义

  • ES2017 标准引入了 async 函数。
  • async 函数就是 Generator 函数的语法糖。

async 函数对 Generator 函数进行了几点改进:

  • 内置执行器
    • Generator 函数的执行必须依靠执行器,而 async 函数自带执行器。
  • 更好的语义
    • async 表示函数里有异步操作。
    • await 表示紧跟在后面的表达式需要等待结果。
  • 更广的适用性
    • yield 命令后面只能是 Thunk 函数或 Promise 对象。
    • async 函数的 await 命令后面,可以是 Promise 对象和原始类型的值(数值、字符串和布尔值,但这时会自动转成立即 resolved 的原始对象)。
  • 返回值是 Promise
    • async 函数的返回值是 Promise 对象,可以用 then 方法执行下一步操作。

Async 基本用法

使用说明

  • 函数前面的 async 关键字,表明该函数内部有异步操作。
  • 调用该函数时,会立即返回一个 Promise 对象。
  • 由于 async 函数返回的是 Promise 对象,可以作为 await 命令的参数。

使用形式

  • 函数声明
async function foo() {}
  • 函数表达式
const foo = async function() {}
  • 对象的方法
let obj = {async foo() {}}
obj.foo().then(...)
  • Class 的方法
class Storage {
  constructor() {
    this.cachePromise = cache.open('avatars')
  }

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

const storage = new Storage()
storage.getAvatar('jack').then(...)
  • 箭头函数
const foo = async () => {}

Async 的语法

async 函数的语法规则总体上比较简单,难点是错误处理机制

返回 Promise 对象

  • async 函数返回一个 Promise 对象。
  • async 函数内部 return 语句返回的值,会成为 then 方法回调函数的参数。
async function f() {
    return 'hello Async 函数';
}

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

// 控制台会打印:hello Async 函数
  • async 函数内部抛出错误,会导致返回的 Promise 对象变为 reject 状态。
async function f() {
    throw new Error('出错了');
}

f().then(
    v => console.log('resolve', v),
    e => console.log('reject', e)
)

// 控制台会打印:reject Error: 出错了

Promise 对象的状态变化

  • async 函数返回的 Promise 对象,必须等到内部所有的 await 命令后面的 Promise 对象执行完,才会发生状态改变,除非遇到 return 语句或者抛出错误。
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)

await 命令

  • 正常情况下,await 命令后面是一个 Promise 对象,返回该对象的结果。如果不是 Promise 对象,就直接返回对应的值。
async function f() {
    return await 123;
}

f().then(console.log)
  • await 命令后面是一个 thenable 对象(即定义了 then 方法的对象),那么 await 会将其等同于 Promise 对象。
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(1000);
    console.log(sleepTime)
})();
  • await 命令后面的 Promise 对象如果变为 reject 状态,则 reject 的参数会被 catch 方法的回调函数接收到。
async function f() {
    await Promise.reject('出错了');
}

f()
    .then(v => console.log(v))
    .catch(e => console.log(e))
  • 任何一个 await 语句后面的 Promise 对象变为 reject 状态,那么整个 async 函数都会中断执行。
async function f() {
    let result = await Promise.reject('先就出错了');
    result = await Promise.resolve('这里不会执行了。。呜呜');
    return result;
}
f().then(console.log).catch(console.log)
  • 将前面的 await 放在 try..catch 结构里面,这样这个异步操作是否成功,都不会中断后面的异步操作。
// 写法一
async function f() {
    try {
        await Promise.reject('出错了')
    } catch (e) {}

    return await Promise.resolve('Ok');
}
f().then(console.log)

// 写法二
async function f() {
    await Promise.reject('出错了').catch(e => console.log(e))
    return await Promise.resolve('Ok');
}
f().then(console.log)

错误处理

  • 如果 await 后面的异步操作出错,那么等同于 async 函数返回的 Promise 的对象被 reject。
async function f() {
    await new Promise(function (resolve, reject) {
        throw new Error('出错了');
    })
}
f().then(
        v => console.log(v),
        e => console.log(e)
    )
    .catch(console.log)
  • 防止出错的方法,就 hi 是将 await 放在 try…catch 代码块中。

使用注意点

  • await 命令后面的 Promise 对象,运行结果可能是 rejected,所以最好把 await 命令放在 try…catch 代码块中。
  • 多个 await 命令后面的异步操作,如果不存在继发关系,最好让它们同时触发,这样就会缩短程序的执行时间。
  • await 命令只能用在 async 函数之中,如果用在普通函数,或者根代码块中,就会报错。
  • async 函数可以保留运行堆栈。

async 函数的实现原理

async 函数就是将 Generator 函数和自动执行器,包装在一个函数里。

async function fn(args) {
  // ...
}

// 等同于

function fn(args) {
  return spawn(function* () {
    // ...
  });
}
声明:本文为原创文章,51blog.xyz和作者拥有版权,如需转载,请注明来源于51blog.xyz并保留原文链接:https://www.51blog.xyz/article/27

文章归档

强烈推荐的PHP全栈开发后台管理系统
buildadmin logo
Thinkphp8 Vue3 Element PLus TypeScript Vite Pinia

🔥BuildAdmin是一个永久免费开源,无需授权即可商业使用,且使用了流行技术栈快速创建商业级后台管理系统。

推荐文章

热门标签

PHP ThinkPHP ThinkPHP5.1 Go Mysql Mysql5.7 Redis Linux CentOS7 Git HTML CSS CSS3 Javascript JQuery Vue LayUI VMware Uniapp 微信小程序 docker wiki Confluence7 学习笔记 uView ES6 Ant Design Pro of Vue React ThinkPHP6.0 chrome 扩展 翻译工具 Nuxt SSR 服务端渲染 scrollreveal.js ThinkPHP8.0 Mac webman 跨域CORS vscode GitHub ECharts Canvas vue3 three.js 微信支付 PHP全栈开发 Python AI 人工智能 AI辅助 工作经验 实战笔记