使用 Promise.all 优化多个异步任务执行

65 阅读1分钟

需求背景

在开发中,我们经常遇到需要同时处理多个异步任务的情况,比如:

  • 同时请求多个用户的 GitHub 仓库信息
  • 多个 API 并行调用
  • 批量数据处理

本文将通过一个实际案例,展示如何使用 Promise.all 优化多个异步任务的执行效率。

初始代码分析

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>GitHub 仓库查询</title>
</head>
<body>
<script>
// 模拟获取 GitHub 仓库信息的异步函数
const getRepos = function(id) {
    return new Promise((resolve, reject) => {
        try {
            setTimeout(() => {
                resolve({id, data: `${id}的仓库数据`})
            }, 2000)
        } catch(err) {
            reject(err)
        }
    })
}

// 批量异步任务 - 原始实现
;(async() => {
    const res = []
    res[0] = await getRepos('LeeAt67');
    res[1] =  await getRepos('shunwuyu');
    console.log(res);
})()
</script>
</body>
</html>

上述代码虽然能完成任务,但存在明显问题:

  1. 顺序执行:第二个请求必须等待第一个完成才开始
  2. 效率低下:总耗时 = 所有请求耗时的总和(约 4 秒)
  3. 代码冗余:重复的 await 语句

Promise.all 的威力

什么是 Promise.all?

Promise.all 是 JavaScript 提供的静态方法,用于处理多个 Promise 对象:

  • 所有 Promise 都成功时返回结果数组
  • 任何 Promise 失败则立即拒绝
  • 结果顺序与输入顺序一致
  • 并行执行所有异步任务

优化后的实现

;(async() => {
    try {
        // 使用 Promise.all 并行执行所有异步任务
        const res = await Promise.all([
            getRepos('LeeAt67'),
            getRepos('shunwuyu'),
            getRepos('wwyyhappy')
        ]);
        
        console.log("所有请求已完成!", res);
        
        // 创建可视化结果展示
        const container = document.createElement('div');
        container.style.cssText = `
            max-width: 800px;
            margin: 2rem auto;
            padding: 20px;
            background: #2d2d2d;
            border-radius: 10px;
            box-shadow: 0 4px 20px rgba(0,0,0,0.5);
            color: #f0f0f0;
            font-family: 'Segoe UI', Tahoma, sans-serif;
        `;
        
        const title = document.createElement('h1');
        title.textContent = 'GitHub 仓库查询结果';
        title.style.textAlign = 'center';
        title.style.color = '#61dafb';
        container.appendChild(title);
        
        // 添加性能对比说明
        const perfNote = document.createElement('div');
        perfNote.innerHTML = `
            <p style="background: #444; padding: 10px; border-radius: 5px;">
                <strong>性能提升:</strong> 
                使用 Promise.all 后,所有请求并行执行,
                总耗时约 2 秒(原顺序执行需 4 秒)
            </p>
        `;
        container.appendChild(perfNote);
        
        res.forEach(user => {
            const card = document.createElement('div');
            card.style.cssText = `
                background: #3a3a3a;
                padding: 15px;
                margin: 15px 0;
                border-radius: 8px;
                border-left: 4px solid #61dafb;
                transition: transform 0.3s;
            `;
            card.innerHTML = `
                <h2 style="color: #61dafb; margin-top: 0;">${user.id}</h2>
                <div style="display: flex; align-items: center;">
                    <div style="background: #4a4a4a; height: 80px; width: 80px; 
                        border-radius: 50%; display: flex; align-items: center; 
                        justify-content: center; margin-right: 15px; font-size: 2rem;">
                        ${user.id.charAt(0)}
                    </div>
                    <div>
                        <p>仓库数量: ${Math.floor(Math.random() * 20) + 5}</p>
                        <p>最近更新: ${new Date().toLocaleDateString()}</p>
                    </div>
                </div>
            `;
            card.addEventListener('mouseenter', () => 
                card.style.transform = 'translateX(5px)');
            card.addEventListener('mouseleave', () => 
                card.style.transform = 'none');
                
            container.appendChild(card);
        });
        
        document.body.innerHTML = '';
        document.body.appendChild(container);
        
    } catch (error) {
        console.error("请求失败:", error);
        document.body.innerHTML = `
            <div style="text-align: center; padding: 50px; color: #ff6b6b;">
                <h1>请求出错</h1>
                <p>${error.message}</p>
            </div>
        `;
    }
})()

关键优化点

  1. 并行执行

    const res = await Promise.all([
         getRepos('LeeAt67'),
         getRepos('shunwuyu'),
         getRepos('wwyyhappy')
    ]);
    
  2. 统一错误处理

    try {
        // Promise.all 代码
    } catch (error) {
        // 处理任何请求中的错误
    }
    
  3. 性能大幅提升

    • 顺序执行:2秒 × 3 = 6秒
    • Promise.all:约2秒(最慢请求的时间)

使用场景建议

适合使用 Promise.all 的情况:

  • 多个独立异步任务
  • 不关心任务执行顺序
  • 需要最大化并行效率

不适合的情况:

  • 任务之间有依赖关系
  • 需要逐个处理结果
  • 任务数量巨大(考虑分批次)

总结

Promise.all 是优化异步代码的利器:

  1. 减少总等待时间
  2. 简化代码结构
  3. 提高应用响应速度
  4. 统一错误处理逻辑