当前位置: 首页 > news >正文

async 和 await 详解

文章目录

  • 一、为什么需要 async 和 await?
  • 二、基本概念
    • 1. 定义和目的
    • 2. 核心特性
    • 3. 执行流程
    • 4. 错误处理
  • 三、底层原理
    • 1. async 函数的转换
    • 2. await 的工作机制
    • 3. Promise 的整合
    • 4. 错误处理
    • 5. 关键点总结
    • 6. 伪代码实现
  • 四、最佳实践与陷阱
  • 五、总结

一、为什么需要 async 和 await?

在传统异步编程中,处理多个异步操作时容易出现“回调地狱”(Callback Hell),代码嵌套层级深,难以维护。Promise 提供了 .then() 链式调用,但依然需要回调函数。async/await 的引入让异步代码的流程更清晰,语法更简洁。

二、基本概念

1. 定义和目的

定义asyncawait是 JavaScript 处理异步操作的语法糖,基于 Promise 实现。

目的: 让异步代码的书写和阅读更接近同步代码,解决"回调地狱" 和 Promise 链式调用冗长的问题。

// Promise 链式调用
fetchData().then((data) => process(data).catch((err) => console.error(err)))//async/await 书写
async function handleData() {try {const data = await fetchData()} catch (error) {console.error(error)}
}

2. 核心特性

  • async 函数:
    • 声明:通过 async function 定义。
    • 返回值:始终返回 Promise。若返回非 Promise 值,会自动包装为 Promise.resolve(value); 若抛出错误,返回 Promise.reject(error)
  • await 表达式:
    • 只能在 async 函数内部使用。
    • 暂停当前函数执行,等待 Promise 完成(resolvedrejected)。
    • 若等待的值非 Promise,直接返回该值。
async function example() {const result = await 42 // 等价于 await Promise.resolve(42)return result // 会被包装为 Promise
}

3. 执行流程

  • 单线程非阻塞: await 暂停的是当前 async 函数的执行,主线程会仍可处理其他任务(如 UI 渲染、定时器等)
  • 事件循环机制: 浏览器引擎在 Promise 完成后将 async 函数推入微任务队列,恢复执行。
console.log('Start')
async function foo() {console.log('Before await')await Promise.resolve() // 暂停当前函数执行console.log('After await')
}
foo()
console.log('End')// Start -> Before await ->  End -> Ater await

4. 错误处理

  • try/catch: 捕获 await 表达式的同步错误 和 Promise 拒绝(reject)
  • 链式.catch(): 在调用async 函数后使用 .catch()
async function fetchData() {try {const response = await fetch('invalid-url')return response.json()} catch (err) {console.error('Fetch failed:', err)throw err // 继续抛出供外部处理}
}fetchData().catch(() => console.log('External handling'))

三、底层原理

1. async 函数的转换

生成器函数: async 函数本质上是生成器函数(Generator)的语法糖。生成器通过 yield 暂停执行,并通过 next() 方法恢复。Babel 等工具将 async 函数转换为生成器,例如:

async function foo() {await bar()
}function* foo() {yield bar()
}

自动执行器: 需要一个执行器(如 co 库)驱动生成器,自动处理 yield 的 Promise, 递归调用 next()throw() 来恢复执行。

2. await 的工作机制

暂停与恢复: 遇到 await 时,引擎将 async 函数暂停,保存当前上下文(变量、执行位置)。背后的生成器通过 yield 暂停,执行器等待 Promise 完成。

微任务队列: 当 Promise 解决(resolve/reject)时,回调被放入微任务队列。当前调用栈清空后,事件循环优先处理微任务,恢复 async 函数执行。

3. Promise 的整合

返回值包装: async 函数始终返回 Promise。若函数返回非 Promise 值,引擎将其包装为 Promise.resolve(value);若抛出异常,返回 Promise.reject(error)。

隐式 Promise 链: 每个 await 生成一个 Promise,按顺序链接。执行器确保后续代码在 Promise 解决后执行。

4. 错误处理

try/catch 模拟: 执行器通过生成器的 throw() 方法将 Promise 的 rejection 转换为 async 函数内的异常,可被 try/catch 捕获。

// 执行器伪代码
try {const result = yield promisegen.next(result) // 正常回复} catch (err) {gen.throw(err) // 抛出错误
}

5. 关键点总结

  • 生成器 + 执行器: async/await 通过生成器暂停/恢复,执行器自动处理异步逻辑。
  • 微任务调度 ​​:Promise 回调通过微任务队列及时恢复,减少延迟。
  • 同步化编码 ​​:以同步写法管理异步流程,避免回调地狱,提升可读性。

6. 伪代码实现

// 将生成器函数转换为返回 Promise 的异步函数(类似 async 函数)
// 将生成器函数转换为返回 Promise 的异步函数(类似 async 函数)
function asyncToGenerator(generatorFunc) {// 返回一个新的函数,该函数接受原生成器函数的参数return function (...args) {// 创建生成器实例(相当于执行生成器函数获取迭代器)const generator = generatorFunc.apply(this, args)// 返回一个 Promise,用于模拟 async 函数的返回值return new Promise((resolve, reject) => {// 定义递归执行的步骤函数// key: 'next' 或 'throw',arg: 传递给生成器的值或错误function step(key, arg) {let result // 存储生成器方法执行结果try {// 执行生成器的 next() 或 throw() 方法// 例如 generator.next(arg) 或 generator.throw(arg)result = generator[key](arg)} catch (err) {// 捕获同步错误,直接拒绝 Promisereturn reject(err)}// 解构生成器返回的迭代结果 { value: any, done: boolean }const { value, done } = resultif (done) {// 生成器执行完成(遇到 return),解析最终结果resolve(value)} else {// 将 yield 的值转换为 Promise(兼容非 Promise 值)Promise.resolve(value).then(// 成功时递归调用 next() 传递结果,继续执行生成器(val) => step('next', val),// 失败时递归调用 throw() 抛出错误,生成器内部可通过 try/catch 捕获(err) => step('throw', err))}}// 首次启动生成器,执行第一个 yieldstep('next')})}
}
  • 闭包结构
    • 返回的新函数保持与原生成器函数相同的参数签名,使得调用方式与 async 函数一致
return function (...args) { ... }
  • 生成器初始化
    • 通过 apply 保留原函数的 this 绑定,传入参数创建生成器实例
    • 相当于执行 const gen = generatorFunc(arg1, arg2)
const generator = generatorFunc.apply(this, args)
  • Promise 封装
    • 外层包裹 Promise,实现 async 函数返回 Promise 的特性
return new Promise((resolve, reject) => { ... })
  • 递归步骤函数

    • key 控制生成器的行为:‘next’ 继续执行,‘throw’ 抛出错误
    • arg 传递值给生成器(yield 返回值或错误对象)
function step(key, arg) { ... }
  • 错误边界处理
    • 捕获生成器方法(next/throw)执行时的同步错误
    • 例如:生成器内部未捕获的 throw new Error
try { ... } catch (err) { reject(err) }
  • 迭代结构解构

    • value:当前 yield 表达式返回的值
    • done:生成器是否执行完成(遇到 return 或执行完毕)
const { value, done } = result
  • 完成状态处理
    • 当生成器执行完成时,将最终结果传递给 Promise 的 resolve
if (done) {resolve(value)
}
  • 异步值处理

    • 统一将 value 转换为 Promise,兼容 yield 42 这类非 Promise 值
    • 相当于 await 的隐式 Promise 包装行为
Promise.resolve(value).then(...)
  • 递归驱动生成器

    • 异步成功后调用 next(val) 恢复生成器,传递解决值
    • 异步失败后调用 throw(err) 让生成器内部可捕获错误
;(val) => step('next', val), (err) => step('throw', err)
  • 启动执行
    • 初始化第一次执行,相当于启动生成器的第一个 yield
step('next')
  • 与 async/await 的对应关系

    代码行为async/await等价操作
    generatorFuncasync function
    yieldsomeValueawait somePromise
    step(“throw”)异步错误被转换为 try/catch 可捕获
    Promise.resolve()await 自动包装非 Promise 值的逻辑

四、最佳实践与陷阱

  • 并行优化
// 低效:顺序执行
const a = await fetchA()
const b = await fetchB()// 高效:并行执行
const [a, b] = await Promise.all([fetchA(), fetchB()])
  • 循环中的 await
// 顺序执行(可能低效)
for (const url of urls) {await fetch(url)
}// 并行执行
await Promise.all(urls.map((url) => fetch(url)))
  • 避免在非 async 函数中使用 await​​:会导致语法错误。

五、总结

async/await是 javascript 处理异步操作的语法糖,基于 Promise 实现,目的是为了让异步代码的书写和阅读更接近同步逻辑,提升可读性和可维护性。它的核心是:一是 async 函数,通过 async function 声明,这个函数会自动将返回值包装为 Promise;二是 await 表达式,它只能在 async 函数内部使用,await 会暂停当前函数的执行,等待其后的 Promise 完成后再继续。执行流程上, await 会释放主线程去处理其他任务,待 Promise 解决后通过事件循环恢复执行;错误处理需通过 try/catch 捕获,避免因未处理的 reject 导致静默失败。实践中需注意优化并行操作(如用 Promise.all 替代顺序 await)、避免循环中不必要的阻塞。底层原理上,主要是使用 Generator + 自动执行器实现,最终在异步场景中实现简洁且高效的代码。

http://www.xdnf.cn/news/162127.html

相关文章:

  • 论文阅读:2025 arxiv Aligning to What? Limits to RLHF Based Alignment
  • Lustre/Scade/Swan 语义性质中的因果性分析介绍
  • ES6 Map/WeakMap/Set/WeakSet 全解指南
  • 2软考系统架构设计师:第一章系统架构概述 - 练习题附答案及超详细解析
  • 直接映射例题及解析
  • 大模型微调与蒸馏的差异性与相似性分析
  • 字节跳动开源数字人模型latentsync1.5,性能、质量进一步优化~
  • 1.1.1 用于排序规则的IComparable接口使用介绍
  • 【MinIO实战】MinIO权限策略设置与上传文件时报错Access Denied排查
  • 03.01、三合一
  • CentOS7 部署 Ollama 全栈指南:构建安全远程大模型服务
  • 【Python】Python中的浅拷贝和深拷贝
  • Halcon算子应用和技巧13
  • Spring AI Alibaba - Milvus 初体验,实现知识库效果
  • SDC命令详解:使用reset_design命令重置设计
  • 力扣热题100题解(c++)—链表
  • Python项目实践:控制台银行系统与词频统计工具开发指南
  • c#简易超市充值卡程序充值消费查余额
  • 升级 Spring Boot CLI
  • 信用中国【国密SM2、SM4加解密】逆向算法分析
  • 【学习笔记】Stata
  • CD32.【C++ Dev】类和对象(22) 内存管理(下)
  • 在线录屏工具(压箱底)-免费高清
  • 基于QT的仿QQ音乐播放器
  • Pygame精灵进阶:动画序列与角色控制
  • 信息论核心概念详解
  • 利用【指针引用】对【非空单循环链表】进行删除操作
  • 服务器虚拟化:技术解析与实践指南
  • 协程(微线程)
  • Kdenlive 中的变形、畸变、透视相关功能