普通视图

发现新文章,点击刷新页面。
今天 — 2026年1月16日首页

Async/Await:让异步像同步一样简单

2026年1月16日 10:00

上一期我们学会了用 Promise 链式调用来摆脱回调地狱。
今天我们再往前迈一步,用 async/await 把异步代码写得像同步代码一样直观

async/await 是 ES2017 引入的语法糖,本质上还是 Promise,但极大提升了代码的可读性。

1. 基本写法

// 普通 Promise 写法
function fetchUser() {
  return fetch('/api/user')
    .then(res => res.json())
    .then(data => data.user);
}

// async/await 写法
async function fetchUser() {
  const res = await fetch('/api/user');
  const data = await res.json();
  return data.user;
}

关键点

  • 在函数前加 async,这个函数就自动返回一个 Promise
  • 在 Promise 前面加 await,表示“等到这个 Promise 完成再继续往下走”
  • 代码从上到下顺序执行,像写同步代码一样

2. 错误处理:使用 try…catch

async function loginFlow(username, password) {
  try {
    const token = await login(username, password);
    const user = await getUserInfo(token);
    const products = await getRecommendations(user.level);
    
    renderProducts(products);
    showSuccess("欢迎回来!");
  } catch (error) {
    console.error("登录流程失败:", error);
    showError("出错了,请稍后重试");
  }
}

优点

  • 只需要一个 try…catch,就能捕获整个流程中任意一步的错误
  • 错误位置清晰,栈追踪更有意义
  • 比 Promise 的 .catch() 更接近我们熟悉的同步错误处理方式

3. 常见使用场景

3.1 顺序执行多个异步操作

async function processOrder() {
  const order = await getOrderFromDB(orderId);
  const payment = await processPayment(order.amount);
  const shipping = await createShippingLabel(payment);
  await sendConfirmationEmail(shipping.trackingNumber);
  
  return shipping;
}

3.2 与 Promise.all 结合实现并发

async function loadUserData(userId) {
  const [profile, posts, friends] = await Promise.all([
    fetch(`/api/profile/${userId}`),
    fetch(`/api/posts/${userId}`),
    fetch(`/api/friends/${userId}`)
  ]);
  
  // 三个请求并发发起,最后一起等待完成
  const profileData = await profile.json();
  const postsData = await posts.json();
  const friendsData = await friends.json();
  
  return { profileData, postsData, friendsData };
}

3.3 在循环中使用 await(注意性能)

// 顺序执行(适合需要前一步结果的情况)
async function processItems(items) {
  for (const item of items) {
    const result = await processSingleItem(item);
    saveResult(result);
  }
}

// 如果不需要顺序,可以先 Promise.all 再循环
async function processItemsInParallel(items) {
  const promises = items.map(item => processSingleItem(item));
  const results = await Promise.all(promises);
  results.forEach(saveResult);
}

4. 常见陷阱与注意事项

问题 错误写法 正确写法 说明
在循环里 await 导致串行慢 forEach(item => await fn(item)) for…ofPromise.all forEach 不等待
忘记 await const data = fetch(url) const data = await fetch(url) 否则得到 Promise 对象
try…catch 范围太小 只 catch 单行 包住整个流程 否则错误漏掉
顶层 await 不支持(旧环境) 包在 async 函数里 现代模块支持顶层 await
性能误用 所有操作都 await 能并行的用 Promise.all 避免不必要的等待

5. 真实业务对比

Promise 链式

login(username, pwd)
  .then(token => getUserInfo(token))
  .then(user => getRecommendations(user.level))
  .then(products => render(products))
  .catch(err => showError(err));

async/await

async function start() {
  try {
    const token = await login(username, pwd);
    const user = await getUserInfo(token);
    const products = await getRecommendations(user.level);
    render(products);
  } catch (err) {
    showError(err);
  }
}

大多数开发者认为后者更清晰、更容易维护。

6. 小结:async/await 的核心价值

  • 可读性:代码结构接近日常同步思维
  • 错误处理:统一的 try…catch
  • 调试友好:断点更容易命中预期位置
  • 与 Promise 完全兼容:该并发时用 Promise.all,该顺序时用 await

一句话总结
async/await 让异步代码看起来像同步代码,但依然保留了异步非阻塞的本质。

下一期我们将把学到的异步知识应用到实际网络请求中:
Fetch API 与异步网络请求 —— 现代浏览器中最常用的数据获取方式。

我们下期见~

留言区互动:
你更喜欢 Promise 链式还是 async/await?
有没有在项目中因为 async/await 写法而修复过 bug 的经历?

昨天以前首页

【开源项目推荐】Biome:让前端代码质量工具链快到飞起来

2026年1月12日 09:40

这是"开源项目推荐"系列的第 2 篇文章,我们将为你介绍那些值得关注、值得使用的优秀开源项目。

为什么推荐这个项目?

在日常开发中,你是否遇到过这样的困境:

每次保存文件都要等待几秒钟的格式化和代码检查 运行一次 lint 需要喝杯茶才能等到结果 团队成员的 Prettier 和 ESLint 配置冲突,合并代码成了噩梦 CI/CD 流水线因为代码检查步骤太慢,拖慢了整个部署流程

今天推荐的项目 Biome,恰好解决了这些痛点——它是一个用 Rust 编写的现代化前端工具链,免费、开源、极速、优雅,堪称前端工具链的"速度之王"。

项目概览

项目信息 详情
项目名称 Biome
GitHub 仓库 biomejs/biome
开源协议 MIT License / Apache 2.0
核心技术 Rust + JavaScript/TypeScript
项目状态 活跃维护中,快速迭代
适用场景 JavaScript/TypeScript 项目、代码格式化、静态分析、CI/CD

核心亮点

1. 令人咋舌的性能表现

Biome 的性能数据足以让任何开发者眼前一亮:

  • 格式化:比 Prettier 快 25 倍,比 parallel-prettier 快 20 倍
  • 代码检查:比 ESLint 快 15 倍,即使在单线程模式下也快 4 倍
  • 扩展性:性能与核心数呈线性扩展,M1 Max 上可达 100 倍速度提升

这不是实验室的理想环境数据,而是基于真实项目的基准测试结果。在你的日常开发中,这意味着每次保存可能只需要几十毫秒,而不是几秒钟。

2. 零配置的极致简化

Biome 的设计哲学是"合理的默认值"。你不需要:

  • 编写复杂的 ESLint 配置文件
  • 调整 Prettier 和 ESLint 的冲突规则
  • 安装和协调多个 npm 包
  • 担心不同工具之间的行为不一致

只需一行命令安装,然后立即使用:

npm install @biomejs/biome
npx biome check --write .

3. 单体架构的优雅设计

传统前端工具链是"拼装车":ESLint 负责检查,Prettier 负责格式化,各自解析各自的语法树。Biome 采用单体架构,一次解析,多次复用。

功能 传统工具链 Biome
代码解析 每个工具独立解析 一次解析,共享语法树
配置管理 多个配置文件 一个 biome.json
规则冲突 经常出现 统一管理,零冲突
性能 线性叠加 协同优化

4. 原生 Rust 的速度优势

Biome 直接编译成原生二进制文件,消除了 JavaScript 运行时的开销:

  • 瞬间启动:没有 Node.js 的初始化延迟
  • 高效内存:编译时的内存管理,无 GC 突然暂停
  • CPU 友好:机器码直接执行,充分利用现代 CPU
  • 跨平台:无需 Node.js 即可运行,部署更灵活

5. 智能并行化

你的开发服务器有 16 个核?Biome 会全部用起来。ESLint 默认单线程,而 Biome 从设计之初就拥抱并行化。

在 CI/CD 流水线中,这意味着你的代码检查步骤可能从几分钟缩短到几十秒,显著提升反馈速度。

技术栈分析

依赖库/技术 作用 优势
Rust 核心实现 原生性能、内存安全、零成本抽象
rowan 语法树库 高效的树形数据结构,支持增量更新
biome_rowan 自定义语法树 针对前端语言优化的语法表示
biome_cli 命令行界面 用户友好的交互体验
biome_lsp 语言服务器协议 编辑器集成支持

实际应用场景

从项目架构可以看出,Biome 支持多种典型场景:

代码质量门禁:在 CI/CD 流水线中作为必检步骤,快速反馈代码问题 大型项目重构:自动化格式化和修复,保证代码风格一致 实时开发体验:保存时自动格式化和检查,几乎无感知延迟 多语言项目:同时支持 JavaScript、TypeScript、JSON、CSS 等多种语言 Monorepo 管理:高效的并行处理,适合大型代码库

使用建议

适合使用的项目

✅ JavaScript/TypeScript 项目 ✅ 追求极致性能的团队 ✅ 大型代码库或 Monorepo ✅ CI/CD 流水线需要快速反馈 ✅ 希望统一工具链配置的团队

需要注意的地方

⚠️ 规则生态还在快速发展,可能不如 ESLint 丰富 ⚠️ 高度定制的规则需求可能需要等待功能完善 ⚠️ 目前不支持 TypeScript 类型检查(推荐配合 TypeScript 使用) ⚠️ 迁移现有项目需要时间适配

快速上手

# 1. 安装 Biome
npm install @biomejs/biome

# 2. 初始化配置(可选)
npx @biomejs/biome init

# 3. 检查并修复代码
npx @biomejs/biome check --write .

# 4. 在 CI 中使用(只检查,不修改)
npx @biomejs/biome ci .

更详细的步骤请参考 快速开始安装与设置

项目评价

优势

✅ 开源免费,MIT/Apache 双协议,无商业限制 ✅ 性能极致,显著提升开发效率 ✅ 零配置开箱即用,学习成本低 ✅ 单体架构,避免工具链冲突 ✅ Rust 编写,内存安全和稳定性有保障 ✅ 活跃的社区和快速的版本迭代

潜在不足

⚠️ 规则生态相对较新,插件数量少于 ESLint ⚠️ TypeScript 类型检查能力有限 ⚠️ 特定领域的规则可能需要等待社区贡献 ⚠️ 文档和最佳实践还在完善中

总结

Biome 是一个重新定义前端工具链性能标准的项目。它没有试图成为"全能型"解决方案,而是专注于将代码格式化和检查做得更快、更简单、更统一。

Rust 的性能优势、单体架构的设计理念、零配置的使用体验,这些让 Biome 成为了前端工具链领域的一股清流。如果你正在被传统工具链的慢速和复杂性困扰,Biome 值得你花时间尝试。

它可能不会立即改变你的开发方式,但节省下来的每一秒钟,都是对开发效率和体验的投资。

❌
❌