Async/Await

async函数

async关键字声明的异步函数。

基本语法

// 声明async函数
async function fetchData() {
    return "东巴文";
}

// 函数表达式
const getData = async function() {
    return "db-w.cn";
};

// 箭头函数
const fetchUser = async () => {
    return { name: "东巴文" };
};

// 类方法
class Api {
    async load() {
        return "数据";
    }
}

async函数返回值

// async函数总是返回Promise
async function greet() {
    return "Hello";
}

console.log(greet());  // Promise { "Hello" }

greet().then(value => {
    console.log(value);  // "Hello"
});

// 返回Promise
async function fetchUser() {
    return fetch("/api/user").then(r => r.json());
}

// 返回值会被Promise.resolve包装
async function getValue() {
    return Promise.resolve("已解决的Promise");
}

getValue().then(v => console.log(v));  // "已解决的Promise"

await表达式

等待Promise解决。

基本用法

// await等待Promise解决
async function fetchData() {
    const response = await fetch("/api/data");
    const data = await response.json();
    return data;
}

// await只能在async函数内使用
async function example() {
    const value = await Promise.resolve("东巴文");
    console.log(value);  // "东巴文"
}

// 错误:不能在普通函数中使用
function wrong() {
    // await Promise.resolve("错误");  // SyntaxError
}

await执行顺序

async function demo() {
    console.log("1. 开始");
    
    const value = await Promise.resolve("东巴文");
    console.log("2. 获取值:", value);
    
    console.log("3. 结束");
}

console.log("a. 调用前");
demo();
console.log("b. 调用后");

// 输出顺序:
// a. 调用前
// 1. 开始
// b. 调用后
// 2. 获取值: 东巴文
// 3. 结束

await非Promise值

async function example() {
    // 非Promise值会被立即解决
    const value1 = await "字符串";
    console.log(value1);  // "字符串"
    
    const value2 = await 123;
    console.log(value2);  // 123
    
    const value3 = await { name: "东巴文" };
    console.log(value3);  // { name: "东巴文" }
    
    // 等价于
    const value4 = await Promise.resolve("值");
}

错误处理

使用try-catch处理异步错误。

try-catch

async function fetchData() {
    try {
        const response = await fetch("/api/data");
        
        if (!response.ok) {
            throw new Error(`HTTP错误: ${response.status}`);
        }
        
        const data = await response.json();
        return data;
    } catch (error) {
        console.error("请求失败:", error);
        throw error;  // 可以重新抛出
    }
}

// 使用
fetchData()
    .then(data => console.log(data))
    .catch(error => console.error("外层捕获:", error));

多个await错误处理

async function process() {
    try {
        const user = await getUser();
        const posts = await getPosts(user.id);
        const comments = await getComments(posts[0].id);
        return comments;
    } catch (error) {
        console.error("处理失败:", error);
        return null;
    }
}

// 分开处理
async function processSeparate() {
    try {
        const user = await getUser();
    } catch (error) {
        console.error("获取用户失败:", error);
        return;
    }
    
    try {
        const posts = await getPosts(user.id);
    } catch (error) {
        console.error("获取文章失败:", error);
        return;
    }
    
    // ...
}

catch方法

// 使用catch方法
async function fetchData() {
    const response = await fetch("/api/data");
    return response.json();
}

fetchData().catch(error => {
    console.error("错误:", error);
});

// 等价于
async function fetchDataWithCatch() {
    try {
        const response = await fetch("/api/data");
        return response.json();
    } catch (error) {
        console.error("错误:", error);
    }
}

并行执行

多个异步操作并行执行。

顺序执行 vs 并行执行

// 顺序执行(慢)
async function sequential() {
    console.time("顺序");
    
    const a = await fetch("/api/a");
    const b = await fetch("/api/b");
    const c = await fetch("/api/c");
    
    console.timeEnd("顺序");  // 约3秒
    return [a, b, c];
}

// 并行执行(快)
async function parallel() {
    console.time("并行");
    
    const [a, b, c] = await Promise.all([
        fetch("/api/a"),
        fetch("/api/b"),
        fetch("/api/c")
    ]);
    
    console.timeEnd("并行");  // 约1秒
    return [a, b, c];
}

Promise.all配合await

async function loadPage() {
    // 并行加载多个数据
    const [user, posts, settings] = await Promise.all([
        fetch("/api/user").then(r => r.json()),
        fetch("/api/posts").then(r => r.json()),
        fetch("/api/settings").then(r => r.json())
    ]);
    
    return { user, posts, settings };
}

// 带错误处理
async function loadPageSafe() {
    try {
        const results = await Promise.allSettled([
            fetch("/api/user").then(r => r.json()),
            fetch("/api/posts").then(r => r.json()),
            fetch("/api/settings").then(r => r.json())
        ]);
        
        const [user, posts, settings] = results.map(r => 
            r.status === "fulfilled" ? r.value : null
        );
        
        return { user, posts, settings };
    } catch (error) {
        console.error("加载失败:", error);
    }
}

部分并行

async function complexFlow() {
    // 第一步:获取用户
    const user = await fetch("/api/user").then(r => r.json());
    
    // 第二步:并行获取用户相关数据
    const [posts, followers, following] = await Promise.all([
        fetch(`/api/posts/${user.id}`).then(r => r.json()),
        fetch(`/api/followers/${user.id}`).then(r => r.json()),
        fetch(`/api/following/${user.id}`).then(r => r.json())
    ]);
    
    return { user, posts, followers, following };
}

循环中的异步

在循环中使用async/await。

顺序执行

// 顺序处理每个项目
async function processItems(items) {
    for (const item of items) {
        await processItem(item);
    }
}

// 获取结果数组
async function fetchAllUrls(urls) {
    const results = [];
    
    for (const url of urls) {
        const response = await fetch(url);
        const data = await response.json();
        results.push(data);
    }
    
    return results;
}

并行执行

// 并行处理所有项目
async function processItemsParallel(items) {
    await Promise.all(items.map(item => processItem(item)));
}

// 获取结果数组
async function fetchAllUrlsParallel(urls) {
    const promises = urls.map(url => 
        fetch(url).then(r => r.json())
    );
    return Promise.all(promises);
}

for...of循环

async function processItems(items) {
    for (const item of items) {
        await processItem(item);
    }
}

// 带索引
async function processWithIndex(items) {
    for (const [index, item] of items.entries()) {
        console.log(`处理第${index}项`);
        await processItem(item, index);
    }
}

forEach注意事项

// 错误:forEach不会等待async
async function wrong() {
    [1, 2, 3].forEach(async (item) => {
        await delay(1000);
        console.log(item);  // 几乎同时输出
    });
    console.log("完成");  // 先输出
}

// 正确:使用for...of
async function correct() {
    for (const item of [1, 2, 3]) {
        await delay(1000);
        console.log(item);  // 每隔1秒输出
    }
    console.log("完成");  // 最后输出
}

// 或者并行
async function parallel() {
    await Promise.all([1, 2, 3].map(async (item) => {
        await delay(1000);
        console.log(item);
    }));
    console.log("完成");
}

顶层await

在模块顶层直接使用await。

基本用法

// 模块顶层(ES模块)
// config.js
const response = await fetch("/api/config");
const config = await response.json();

export default config;

// 使用模块
import config from "./config.js";
console.log(config);  // 配置已加载

动态加载

// 动态导入模块
const module = await import("./heavy-module.js");

// 条件加载
const locale = await import(`./locales/${navigator.language}.js`);

// 依赖初始化
const database = await connectDatabase();

模拟顶层await

// 在不支持的环境中使用IIFE
(async () => {
    const config = await fetch("/api/config").then(r => r.json());
    console.log(config);
})();

// 或使用then
fetch("/api/config")
    .then(r => r.json())
    .then(config => {
        console.log(config);
    });

实际应用示例

API请求封装

class ApiClient {
    constructor(baseUrl) {
        this.baseUrl = baseUrl;
    }
    
    async request(path, options = {}) {
        const url = `${this.baseUrl}${path}`;
        
        try {
            const response = await fetch(url, {
                headers: {
                    "Content-Type": "application/json",
                    ...options.headers
                },
                ...options
            });
            
            if (!response.ok) {
                throw new Error(`HTTP ${response.status}`);
            }
            
            return response.json();
        } catch (error) {
            console.error("请求失败:", error);
            throw error;
        }
    }
    
    async get(path) {
        return this.request(path);
    }
    
    async post(path, data) {
        return this.request(path, {
            method: "POST",
            body: JSON.stringify(data)
        });
    }
}

// 使用
const api = new ApiClient("/api");

async function loadUser() {
    const user = await api.get("/user/1");
    console.log(user);
}

重试机制

async function fetchWithRetry(url, options = {}, retries = 3) {
    for (let i = 0; i < retries; i++) {
        try {
            const response = await fetch(url, options);
            if (!response.ok) {
                throw new Error(`HTTP ${response.status}`);
            }
            return response.json();
        } catch (error) {
            if (i === retries - 1) {
                throw error;
            }
            console.log(`重试 ${i + 1}/${retries}`);
            await delay(1000 * (i + 1));  // 递增延迟
        }
    }
}

function delay(ms) {
    return new Promise(resolve => setTimeout(resolve, ms));
}

// 使用
const data = await fetchWithRetry("/api/data", {}, 3);

并发控制

async function limitConcurrency(tasks, limit = 3) {
    const results = [];
    const executing = [];
    
    for (const task of tasks) {
        const promise = Promise.resolve().then(() => task());
        results.push(promise);
        
        if (limit <= tasks.length) {
            const exec = promise.then(() => {
                executing.splice(executing.indexOf(exec), 1);
            });
            executing.push(exec);
            
            if (executing.length >= limit) {
                await Promise.race(executing);
            }
        }
    }
    
    return Promise.all(results);
}

// 使用
const urls = ["/api/1", "/api/2", "/api/3", "/api/4", "/api/5"];
const tasks = urls.map(url => () => fetch(url).then(r => r.json()));

const results = await limitConcurrency(tasks, 2);  // 最多2个并发

下一步

掌握了Async/Await后,让我们继续学习:

  1. 异步迭代 - 学习异步迭代器
  2. Ajax - 学习Ajax请求
  3. [Fetch API](./50_Fetch API.md) - 学习现代网络请求

东巴文(db-w.cn) - 让编程学习更简单

🎯 东巴文寄语:async/await是Promise的语法糖,让异步代码看起来像同步代码,大大提高了代码可读性。在 db-w.cn,我们帮你掌握现代异步编程的最佳实践!