← JavaScript Promise链式调用 JavaScript Promise.allSettled() →

JavaScript Promise.any()

原创 2026-03-14 JavaScript 已有人查阅

JavaScript Promise.any() 方法详解:获取第一个成功的Promise

处理多个异步任务时,有时我们只关心第一个成功的结果,比如从多个CDN加载资源、调用多个备用API接口。这种场景下,Promise.any() 就是最合适的选择。

Promise.any() 是什么?

Promise.any() 接收一个Promise数组,返回第一个成功(fulfilled)的Promise结果。如果所有Promise都失败,则返回一个AggregateError(聚合错误)。

Promise.any([promise1, promise2, promise3])
    .then(firstResult => {
        console.log('第一个成功的结果:', firstResult);
    })
    .catch(error => {
        console.log('所有Promise都失败了:', error);
    });

基本语法

Promise.any(iterable);
  • iterable:一个可迭代对象,是Promise数组

  • 返回值:一个新的Promise

    • 如果有任何一个Promise成功,则返回这个成功的结果

    • 如果所有Promise都失败,则返回一个拒绝的Promise,带有AggregateError

三种典型场景

场景1:所有Promise都成功

代码号学习: 多个接口同时请求
const api1 = new Promise((resolve) => {
    setTimeout(() => {
        console.log('API1 响应慢,2秒后返回');
        resolve('数据源A: 用户信息');
    }, 2000);
});

const api2 = new Promise((resolve) => {
    setTimeout(() => {
        console.log('API2 响应快,1秒后返回');
        resolve('数据源B: 用户信息');
    }, 1000);
});

const api3 = new Promise((resolve) => {
    setTimeout(() => {
        console.log('API3 响应中等,1.5秒后返回');
        resolve('数据源C: 用户信息');
    }, 1500);
});

Promise.any([api1, api2, api3])
    .then(result => {
        console.log('最终使用的数据:', result);
        // 输出: 最终使用的数据: 数据源B: 用户信息
        // 因为API2最快成功
    });

场景2:部分成功、部分失败

const task1 = new Promise((resolve, reject) => {
    setTimeout(() => {
        console.log('任务1 失败');
        reject('任务1出错');
    }, 500);
});

const task2 = new Promise((resolve) => {
    setTimeout(() => {
        console.log('任务2 成功');
        resolve('任务2的结果');
    }, 1000);
});

const task3 = new Promise((resolve) => {
    setTimeout(() => {
        console.log('任务3 成功');
        resolve('任务3的结果');
    }, 1500);
});

Promise.any([task1, task2, task3])
    .then(result => {
        console.log('第一个成功的是:', result);
        // 输出: 第一个成功的是: 任务2的结果
        // task1虽然先失败,但被忽略了,等到task2成功就返回
    });

场景3:所有Promise都失败

const p1 = new Promise((_, reject) => {
    setTimeout(() => reject('错误1'), 1000);
});

const p2 = new Promise((_, reject) => {
    setTimeout(() => reject('错误2'), 800);
});

const p3 = new Promise((_, reject) => {
    setTimeout(() => reject('错误3'), 1200);
});

Promise.any([p1, p2, p3])
    .then(result => {
        console.log('成功:', result); // 不会执行到这里
    })
    .catch(error => {
        console.log('错误类型:', error.name); // AggregateError
        console.log('所有错误:', error.errors); // ['错误2', '错误1', '错误3']
        // 注意:errors数组顺序按完成时间排列,不是输入顺序
    });

实际应用场景

1. 多CDN资源加载

// 从多个CDN加载同一资源,谁快用谁
function loadScriptFromCDN(url) {
    return new Promise((resolve, reject) => {
        const script = document.createElement('script');
        script.src = url;
        script.onload = () => resolve(url);
        script.onerror = () => reject(`加载失败: ${url}`);
        document.head.appendChild(script);
    });
}

const cdn1 = loadScriptFromCDN('https://cdn1.example.com/library.js');
const cdn2 = loadScriptFromCDN('https://cdn2.example.com/library.js');
const cdn3 = loadScriptFromCDN('https://cdn3.example.com/library.js');

Promise.any([cdn1, cdn2, cdn3])
    .then(loadedUrl => {
        console.log(`脚本从 ${loadedUrl} 加载成功`);
        // 初始化应用
        initializeApp();
    })
    .catch(aggregateError => {
        console.error('所有CDN都挂了,请检查网络');
        showOfflineMessage();
    });

2. 备用API接口

// 主API挂了自动切换到备用API
function fetchUserData(userId) {
    const primaryAPI = fetch(`https://api.example.com/users/${userId}`)
        .then(res => res.json());
    
    const backupAPI = fetch(`https://backup-api.example.com/users/${userId}`)
        .then(res => res.json());
    
    const emergencyAPI = fetch(`https://emergency.example.com/users/${userId}`)
        .then(res => res.json());
    
    return Promise.any([primaryAPI, backupAPI, emergencyAPI])
        .then(userData => {
            console.log('获取用户数据成功');
            return userData;
        })
        .catch(error => {
            console.log('所有API都不可用');
            return { id: userId, name: '游客', offline: true };
        });
}

// 使用
fetchUserData(123)
    .then(user => renderUserInfo(user));

3. 最快响应的搜索服务

function searchFromSource(source, keyword) {
    return fetch(`/api/search/${source}q=${keyword}`)
        .then(res => res.json())
        .then(data => ({
            source,
            results: data
        }));
}

function searchAll(keyword) {
    const google = searchFromSource('google', keyword);
    const baidu = searchFromSource('baidu', keyword);
    const bing = searchFromSource('bing', keyword);
    
    return Promise.any([google, baidu, bing])
        .then(fastestResult => {
            console.log(`最快响应来自: ${fastestResult.source}`);
            return fastestResult.results;
        })
        .catch(() => {
            console.log('所有搜索引擎都挂了');
            return [];
        });
}

// 使用
searchAll('JavaScript教程')
    .then(results => displayResults(results));

Promise.any() 与他并发方法的区别

方法 特点 适用场景
Promise.any() 返回第一个成功的结果 只需要一个成功的,不关心他
Promise.race() 返回第一个完成的结果(不论成功失败) 超时控制、竞速
Promise.all() 等待所有成功,一个失败就整体失败 多个并行请求,缺一不可
Promise.allSettled() 等待所有完成,返回每个的结果 需要知道所有任务的结果
 
// 对比示例
const fastSuccess = new Promise(resolve => setTimeout(() => resolve('成功A'), 100));
const fastFail = new Promise((_, reject) => setTimeout(() => reject('失败B'), 50));
const slowSuccess = new Promise(resolve => setTimeout(() => resolve('成功C'), 200));

// Promise.any - 只关心第一个成功
Promise.any([fastFail, fastSuccess, slowSuccess])
    .then(result => console.log('any:', result)); // any: 成功A

// Promise.race - 第一个完成的(不论成功失败)
Promise.race([fastFail, fastSuccess, slowSuccess])
    .then(result => console.log('race成功:', result))
    .catch(error => console.log('race失败:', error)); // race失败: 失败B

// Promise.all - 所有成功才成功
Promise.all([fastSuccess, slowSuccess])
    .then(results => console.log('all:', results)); // all: ['成功A', '成功C']

// Promise.allSettled - 等待所有完成
Promise.allSettled([fastFail, fastSuccess])
    .then(results => console.log('allSettled:', results));
    // allSettled: [
    //   { status: 'rejected', reason: '失败B' },
    //   { status: 'fulfilled', value: '成功A' }
    // ]

个人经验:项目中经常用Promise.any()处理那些"多个备选,谁快用谁"的场景。比如用户上传图片时,同时传给多个图床,第一个上传成功的就拿来用。既提高了成功率,又保证了速度。

处理AggregateError

当所有Promise都失败时,catch到的错误是AggregateError,它有特殊的errors属性:

function testAllFail() {
    const promises = [
        Promise.reject('错误1'),
        Promise.reject('错误2'),
        Promise.reject('错误3')
    ];
    
    return Promise.any(promises);
}

testAllFail()
    .catch(err => {
        if (err.name === 'AggregateError') {
            console.log('总共失败数量:', err.errors.length);
            err.errors.forEach((reason, index) => {
                console.log(`失败${index + 1}:`, reason);
            });
            
            // 可以在这里进行降级处理
            return getDefaultData();
        }
    });

空数组的处理

传入空数组时,Promise.any() 会立即返回一个拒绝的Promise:

Promise.any([])
    .then(result => {
        console.log('成功:', result); // 不会执行
    })
    .catch(error => {
        console.log('空数组错误:', error.name); // AggregateError
        console.log('错误信息:', error.message); // All promises were rejected
        console.log('errors数组:', error.errors); // [] 空数组
    });

课程知识要点

  • 核心功能:Promise.any() 返回第一个成功的Promise结果

  • 忽略失败:只要有一个成功,就忽略他所有失败

  • 全失败处理:所有Promise都失败时,返回AggregateError

  • AggregateError:特殊的错误类型,errors属性包含所有失败原因

  • 适用场景:多CDN加载、备用API、最快响应服务

  • 与race的区别:race返回第一个完成的(失败),any只返回第一个成功的

  • 空数组行为:立即返回拒绝的Promise,带有AggregateError

个人建议:用Promise.any()时一定要考虑全失败的情况,catch里做降级处理。如果不需要关心失败,只想拿第一个成功的结果,any比race更合适,因为race拿到失败的。

← JavaScript Promise链式调用 JavaScript Promise.allSettled() →
分享笔记 (共有 篇笔记)
验证码:
微信公众号