深入理解 JavaScript 中的异步编程:从回调到 async/await

内容分享10小时前发布
0 0 0

深入理解 JavaScript 中的异步编程:从回调到 async/await

在现代 Web 开发中,异步操作无处不在——无论是从服务器获取数据、读取本地文件,还是处理用户交互。JavaScript 作为一门单线程语言,通过多种机制支持异步编程。本文将带你回顾异步编程的发展历程,并重点解析 async/await 这一 ES8 引入的强劲语法糖。


1. 回调函数(Callback)时代:最初的异步方案

早期的 JavaScript 主要依赖回调函数来处理异步操作。例如,在 Node.js 中使用 fs.readFile 读取文件:

fs.readFile('./1.html', 'utf-8', (err, data) => {
    if (err) {
        console.log(err);
        return;
    }
    console.log(data);
    console.log(111);
});

这种方式简单直接,但存在明显问题:

  • 回调地狱(Callback Hell) :多层嵌套导致代码难以阅读和维护。
  • 错误处理分散:每个回调都需要单独处理错误。

2. Promise:ES6 带来的结构化异步

为了解决回调地狱,ES6 引入了 Promise 对象,它代表一个异步操作的最终完成(或失败)及其结果值。

我们可以将 fs.readFile 封装成一个 Promise:

const p = new Promise((resolve, reject) => {
    fs.readFile('./1.html', 'utf-8', (err, data) => {
        if (err) {
            reject(err);
            return;
        }
        resolve(data);
    });
});

p.then(data => {
    console.log(data);
    console.log(111);
}).catch(err => {
    console.error(err);
});

Promise 的优势:

  • 链式调用(.then().then())
  • 统一的错误处理(.catch())
  • 更清晰的异步流程控制

但 .then() 链依旧不够“同步感”,尤其在复杂逻辑中仍显繁琐。


3. async / await:ES8 的终极优雅方案

ES8(ECMAScript 2017)引入了 async 和 await,让异步代码写起来像同步代码一样直观。

基本用法

  • async 用于声明一个函数为异步函数,该函数总是返回一个 Promise
  • await 只能在 async 函数内部使用,用于“等待”一个 Promise 被 resolve,并将其结果赋值给变量。

例如,封装后的文件读取可以这样写:

const main = async () => {
    try {
        const html = await p; // 等待 Promise 完成
        console.log(html);
        console.log(111);
    } catch (err) {
        console.error(err);
    }
};
main();

再列如,从 GitHub API 获取用户仓库信息:

const main = async () => {
    try {
        const res = await fetch('https://api.github.com/users/shunwuyu/repos');
        const data = await res.json();
        console.log(data);
    } catch (error) {
        console.error('请求失败:', error);
    }
};
main();

优势总结

特性

说明

可读性强

代码结构接近同步逻辑,易于理解和调试

错误处理统一

使用 try…catch 捕获异步错误

避免回调地狱

不再需要层层嵌套 .then()

与现有 Promise 兼容

await 后可接任何 Promise


4. 实际应用场景对比

以获取 GitHub 用户仓库为例:

  • 传统 Promise 链式写法
  • fetch('https://api.github.com/users/shunwuyu/repos') .then(res => res.json()) .then(data => console.log(data)) .catch(err => console.error(err));
  • async/await 写法
  • const getRepos = async () => { try { const res = await fetch('https://api.github.com/users/shunwuyu/repos'); const data = await res.json(); console.log(data); } catch (err) { console.error(err); } }; getRepos();

后者更接近自然语言:“先获取响应,再解析 JSON,最后打印数据”。


5. 注意事项

  • await 只能在 async 函数内使用。
  • async 函数总是返回 Promise,即使你 return 一个普通值。
  • 多个不相关的异步操作应避免串行 await,可使用 Promise.all() 并行处理以提升性能。
// ❌ 低效:串行执行
const a = await fetch(url1);
const b = await fetch(url2);

// ✅ 高效:并行执行
const [res1, res2] = await Promise.all([fetch(url1), fetch(url2)]);
© 版权声明

相关文章

暂无评论

none
暂无评论...