async/await 用法

Async/Await 用法详解

asyncawait 是现代 JavaScript(ES2017+)引入的语法,用于简化异步编程,基于 Promise 提供更直观的代码结构。它让异步代码看起来像同步代码,提高可读性和维护性。本指南将详细讲解 async/await 的用法、场景、注意事项,并提供清晰示例。以下内容假设您熟悉 JavaScript 和 Promise。


1. 基本概念

  • async 关键字
  • 用于声明异步函数。
  • async 函数总是返回一个 Promise 对象(即使返回值不是 Promise,会自动包装为 resolved 的 Promise)。
  • 语法:async function functionName() { ... }
  • await 关键字
  • 只能在 async 函数内使用。
  • 用于暂停执行,等待 Promise 解析(resolve)或拒绝(reject)。
  • await 后接 Promise 对象,返回其 resolved 值。

核心机制async/await 是 Promise 的语法糖,简化 .then().catch() 的链式调用。


2. 基本用法

2.1 简单示例

以下是一个使用 async/await 模拟异步请求的例子:

// 模拟异步函数(返回 Promise)
function fetchData() {
    return new Promise((resolve) => {
        setTimeout(() => resolve("Data fetched!"), 1000);
    });
}

// 异步函数
async function getData() {
    console.log("Starting...");
    const result = await fetchData(); // 等待 Promise 解析
    console.log(result); // 输出 "Data fetched!"
    console.log("Finished!");
}

// 调用
getData();

输出(1秒后):

Starting...
Data fetched!
Finished!

解释

  • async function getData() 声明异步函数。
  • await fetchData() 暂停执行,直到 fetchData 的 Promise 解析。
  • result 接收 Promise 的 resolved 值(”Data fetched!”)。

2.2 与 Promise 对比

使用 .then() 的写法:

function getDataWithPromise() {
    console.log("Starting...");
    fetchData()
        .then(result => {
            console.log(result);
            console.log("Finished!");
        });
}

async/await 使代码更像同步逻辑,减少嵌套,提高可读性。


3. 错误处理

await 的 Promise 可能被拒绝(reject),需要用 try...catch 捕获错误。

function fetchDataWithError() {
    return new Promise((resolve, reject) => {
        setTimeout(() => reject(new Error("Fetch failed!")), 1000);
    });
}

async function getData() {
    try {
        console.log("Starting...");
        const result = await fetchDataWithError();
        console.log(result); // 不会执行
    } catch (error) {
        console.error("Error:", error.message); // 输出 "Error: Fetch failed!"
    }
    console.log("Finished!");
}

getData();

输出(1秒后):

Starting...
Error: Fetch failed!
Finished!

说明

  • try 块包含 await 语句,捕获可能的错误。
  • catch 块处理 Promise 的 reject 状态。
  • 等价于 .catch(error => console.error(error))

4. 高级用法

4.1 串行执行(Sequential)

多个异步操作按顺序执行:

async function fetchMultiple() {
    try {
        const data1 = await fetchData("Data 1");
        console.log(data1);
        const data2 = await fetchData("Data 2");
        console.log(data2);
    } catch (error) {
        console.error("Error:", error.message);
    }
}

function fetchData(value) {
    return new Promise(resolve => setTimeout(() => resolve(value), 1000));
}

fetchMultiple();

输出(每秒输出一行):

Data 1
Data 2

说明await 使异步调用按顺序执行,第二个 await 等待第一个完成。

4.2 并行执行(Parallel)

使用 Promise.all 同时执行多个异步操作:

async function fetchParallel() {
    try {
        const [data1, data2] = await Promise.all([
            fetchData("Data 1"),
            fetchData("Data 2")
        ]);
        console.log(data1, data2);
    } catch (error) {
        console.error("Error:", error.message);
    }
}

fetchParallel();

输出(1秒后同时输出):

Data 1 Data 2

说明

  • Promise.all 接受一个 Promise 数组,返回所有 Promise 的 resolved 值数组。
  • 比串行更快,因为两个 fetchData 同时执行。

4.3 结合循环

在循环中使用 await

async function processItems(items) {
    for (const item of items) {
        const result = await fetchData(item);
        console.log(result);
    }
}

processItems(["Item 1", "Item 2"]);

输出(每秒输出一行):

Item 1
Item 2

并行循环(更快):

async function processItemsParallel(items) {
    const promises = items.map(item => fetchData(item));
    const results = await Promise.all(promises);
    results.forEach(result => console.log(result));
}

5. 实际应用场景

5.1 HTTP 请求

使用 fetch API 获取数据:

async function fetchUser(userId) {
    try {
        const response = await fetch(`https://jsonplaceholder.typicode.com/users/${userId}`);
        if (!response.ok) throw new Error("Network error");
        const user = await response.json();
        console.log("User:", user.name);
    } catch (error) {
        console.error("Error:", error.message);
    }
}

fetchUser(1);

输出User: Leanne Graham(假设 API 返回该数据)。

5.2 超时处理

结合 setTimeout 实现超时:

function timeout(ms) {
    return new Promise((_, reject) => {
        setTimeout(() => reject(new Error("Timeout")), ms);
    });
}

async function fetchWithTimeout() {
    try {
        const result = await Promise.race([
            fetchData("Data"),
            timeout(500) // 500ms 超时
        ]);
        console.log(result);
    } catch (error) {
        console.error("Error:", error.message);
    }
}

fetchWithTimeout();

输出(如果 fetchData 超过 500ms):Error: Timeout


6. 好处

  1. 可读性强
  • 代码接近同步写法,避免 .then 嵌套(回调地狱)。
  • 逻辑清晰,易于维护。
  1. 错误处理简洁
  • try...catch 统一处理同步和异步错误。
  • .catch 链更直观。
  1. 灵活性
  • 支持串行、并行、循环等多种异步模式。
  • 兼容所有 Promise-based API(如 fetch、Node.js 异步函数)。

7. 注意事项

  1. 只能在 async 函数内使用 await
  • 在普通函数中使用 await 会抛出语法错误。
  • 顶层 await(Top-level await)仅在 ES Modules 或特定环境(如 Node.js REPL)支持。
  1. 性能考虑
  • 串行 await 可能导致等待时间累加,必要时用 Promise.all
  • 避免在循环中逐个 await,考虑并行处理。
  1. 错误处理
  • 未捕获的错误会导致 Promise 拒绝,需用 try...catch
  • Promise.all 中任一 Promise 拒绝,整个操作失败(可用 Promise.allSettled 替代)。
  1. 兼容性
  • async/await 在现代浏览器和 Node.js(v7.6+)广泛支持。
  • 老旧环境需 Babel 转译。

8. 常见问题与故障排除

问题原因解决方案
SyntaxError: await is only valid in async function在非 async 函数中使用 await确保函数声明为 async
异步操作顺序错误过多串行 await使用 Promise.all 并行执行。
未捕获错误导致程序崩溃缺少 try...catch始终用 try...catch 包裹 await
await 后返回 Promise误认为 await 返回非 Promise 值await 总是解析 Promise 的 resolved 值。

9. 高级示例:链式异步操作

模拟用户数据和帖子加载:

async function fetchUserAndPosts(userId) {
    try {
        // 获取用户
        const userResponse = await fetch(`https://jsonplaceholder.typicode.com/users/${userId}`);
        if (!userResponse.ok) throw new Error("Failed to fetch user");
        const user = await userResponse.json();
        console.log("User:", user.name);

        // 获取用户帖子
        const postsResponse = await fetch(`https://jsonplaceholder.typicode.com/posts?userId=${userId}`);
        if (!postsResponse.ok) throw new Error("Failed to fetch posts");
        const posts = await postsResponse.json();
        console.log("Posts count:", posts.length);

        return { user, posts };
    } catch (error) {
        console.error("Error:", error.message);
        throw error; // 可选择抛出错误供调用者处理
    }
}

async function main() {
    const result = await fetchUserAndPosts(1);
    console.log("Result:", result);
}

main();

输出

User: Leanne Graham
Posts count: 10
Result: { user: {...}, posts: [...] }

说明:展示了 async/await 在真实 API 调用中的链式操作和错误处理。


10. 总结

  • 定义async 声明异步函数,返回 Promise;await 暂停执行,解析 Promise。
  • 用法:简化异步代码,支持串行、并行、循环、错误处理。
  • 好处:可读性强、错误处理简洁、灵活性高。
  • 场景:HTTP 请求、文件操作、数据库查询等。
  • 注意:需在 async 函数内使用,合理管理串行/并行,始终处理错误。

如果您需要特定场景的 async/await 示例(如 Node.js 文件操作、数据库查询)或想深入某方面(如性能优化),请告诉我,我可以进一步定制代码!

类似文章

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注