← JavaScript事件循环 JavaScript Promise链式调用 →

JavaScript Promise

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

JavaScript Promise 指南:告别回调地狱的异步编程方案

还记得刚学JavaScript异步编程时,被嵌套回调支配的恐惧吗?一层套一层的回调函数,代码又丑又难调试。后来Promise的出现,简直拯救了我的代码质量。

Promise是什么?

Promise(承诺) 是JavaScript中表示异步操作最终完成或失败的对象。简单说,它就像你在餐厅点餐后拿到的小票——现在还没拿到饭(异步操作进行中),但凭着这张小票,将来要么成功取餐(操作成功),要么被告知卖完了(操作失败)。

// 创建最简单的Promise
const codePromise = new Promise(function(resolve, reject) {
    // 模拟异步操作
    let success = true;
    if (success) {
        resolve("代码号学习: 操作成功");
    } else {
        reject("代码号学习: 出错了");
    }
});

Promise的三种状态

Promise的状态一旦改变,就凝固了(不会再变):

  1. 待定(Pending):初始状态,异步操作还没完成

  2. 已兑现(Fulfilled):操作成功,调用resolve()

  3. 已拒绝(Rejected):操作失败,调用reject()

个人经验:调试Promise时,我最喜欢在resolve/reject前后加console.log,观察状态变化。这比猜测执行顺序靠谱多了。

Promise的核心方法

Promise.all() - 等待所有Promise完成

接收Promise数组,等所有都成功才返回结果,有一个失败就立即进入catch。

// 项目开发场景:同时加载多个组件的数据
const loadUser = Promise.resolve({id: 1, name: '张三'});
const loadPosts = fetch('/api/posts').then(res => res.json());
const loadComments = fetch('/api/comments').then(res => res.json());

Promise.all([loadUser, loadPosts, loadComments])
    .then(([user, posts, comments]) => {
        console.log('所有数据加载完成:', {user, posts, comments});
    })
    .catch(error => {
        console.error('某个请求失败了:', error);
        // 提示用户重试或显示部分数据
    });

Promise.allSettled() - 等待所有Promise结束

不管成功还是失败,都会等所有Promise结束,返回每个Promise的结果状态。

const promises = [
    Promise.resolve('成功A'),
    Promise.reject('失败B'),
    Promise.resolve('成功C')
];

Promise.allSettled(promises)
    .then(results => {
        results.forEach((result, index) => {
            if (result.status === 'fulfilled') {
                console.log(`第${index + 1}个成功:`, result.value);
            } else {
                console.log(`第${index + 1}个失败:`, result.reason);
            }
        });
    });
// 输出:
// 第1个成功: 成功A
// 第2个失败: 失败B
// 第3个成功: 成功C

Promise.race() - 竞速模式

返回完成的Promise结果(不管是成功还是失败)。

// 实用场景:请求超时控制
function fetchWithTimeout(url, timeout = 5000) {
    return Promise.race([
        fetch(url),
        new Promise((_, reject) => 
            setTimeout(() => reject(new Error('请求超时')), timeout)
        )
    ]);
}

// 使用
fetchWithTimeout('https://api.example.com/data', 3000)
    .then(response => console.log('收到响应'))
    .catch(error => console.log('请求失败:', error.message));

Promise.any() - 只要一个成功

跟race类似,但只关心成功的结果。只有所有Promise都失败时才抛出异常。

// 实际场景:使用多个CDN源,哪个可用用哪个
const cdn1 = fetch('https://cdn1.example.com/library.js');
const cdn2 = fetch('https://cdn2.example.com/library.js');
const cdn3 = fetch('https://cdn3.example.com/library.js');

Promise.any([cdn1, cdn2, cdn3])
    .then(response => {
        console.log('至少有一个CDN加载成功');
        return response.text();
    })
    .catch(error => {
        console.log('所有CDN都挂了:', error);
        // 降级处理
    });

Promise.resolve() 和 Promise.reject()

快速创建已成功或已失败的Promise,常用于测试或函数返回值统一。

// 缓存场景:如果缓存有数据,直接返回Promise.resolve
function getUserData(userId) {
    const cache = getUserCache(userId);
    if (cache) {
        return Promise.resolve(cache); // 返回成功的Promise
    }
    return fetch(`/api/user/${userId}`).then(res => res.json());
}

// 错误处理场景
function validateInput(data) {
    if (!data.name) {
        return Promise.reject(new Error('姓名不能为空'));
    }
    return Promise.resolve(data);
}

Promise.finally() - 无论成功失败都执行

清理工作的好地方,比如关闭加载动画、释放资源。

function showData() {
    showLoadingSpinner(); // 显示加载中
    
    fetch('/api/data')
        .then(response => response.json())
        .then(data => renderData(data))
        .catch(error => showErrorMessage(error))
        .finally(() => {
            hideLoadingSpinner(); // 无论成功失败都隐藏加载动画
        });
}

为什么要用Promise而不是传统回调?

1. 避免回调地狱

// 糟糕的写法:回调地狱
getUser(function(user) {
    getPosts(user.id, function(posts) {
        getComments(posts[0].id, function(comments) {
            renderUI(user, posts, comments);
        }, errorHandler);
    }, errorHandler);
}, errorHandler);

// 使用Promise:链式调用
getUser()
    .then(user => getPosts(user.id))
    .then(posts => getComments(posts[0].id))
    .then(comments => renderUI(user, posts, comments))
    .catch(errorHandler);

个人建议:遇到多层嵌套回调,第一反应就应该是用Promise重构。代码可读性提升不止一个档次。

2. 错误处理更集中

Promise的错误会沿着链往下传递,之后统一处理:

doSomething()
    .then(result => doSomethingElse(result))
    .then(newResult => doThirdThing(newResult))
    .then(finalResult => console.log(finalResult))
    .catch(error => {
        // 任何步骤出错都会来到这里
        console.error('操作失败:', error);
        // 可以在这里统一提示用户
    });

3. 组合异步操作更方便

用Promise.all、Promise.race等方法,轻松管理多个异步任务:

// 并行加载多个资源
const loadResources = Promise.all([
    loadScript('/js/utils.js'),
    loadStyle('/css/style.css'),
    loadTemplate('/tpl/main.html')
]);

loadResources.then(() => {
    console.log('资源加载完成,可以初始化应用');
});

Promise与async/await的配合

Promise是基础,async/await是语法糖,让异步代码更像同步代码:

// 用async/await改写上面的链式调用
async function loadUserData() {
    try {
        const user = await getUser();
        const posts = await getPosts(user.id);
        const comments = await getComments(posts[0].id);
        renderUI(user, posts, comments);
    } catch (error) {
        errorHandler(error);
    }
}

个人观点:新项目我更喜欢用async/await写异步代码,代码更简洁直观。但理解Promise的底层原理很重要,因为async/await本质就是Promise的封装。

项目开发中的Promise模式

重试机制

function fetchWithRetry(url, maxRetries = 3) {
    return new Promise((resolve, reject) => {
        function attempt(retryCount) {
            fetch(url)
                .then(resolve)
                .catch(error => {
                    if (retryCount < maxRetries) {
                        console.log(`第${retryCount + 1}次失败,重试中...`);
                        setTimeout(() => attempt(retryCount + 1), 1000 * retryCount);
                    } else {
                        reject(error);
                    }
                });
        }
        attempt(0);
    });
}

超时控制(增强版)

function promiseWithTimeout(promise, timeout, errorMessage = '操作超时') {
    let timeoutId;
    const timeoutPromise = new Promise((_, reject) => {
        timeoutId = setTimeout(() => {
            reject(new Error(errorMessage));
        }, timeout);
    });
    
    return Promise.race([promise, timeoutPromise])
        .finally(() => clearTimeout(timeoutId));
}

知识点

  • Promise状态:pending(等待中)、fulfilled(成功)、rejected(失败),不可逆

  • 常用方法:.then()处理成功,.catch()处理失败,.finally()收尾

  • 组合方法:Promise.all(全成功)、Promise.allSettled(全结束)、Promise.race(最快一个)、Promise.any(最快成功)

  • 优势:链式调用避免嵌套、统一错误处理、方便组合异步操作

  • 与async/await:async函数返回Promise,await等待Promise结果,try/catch处理错误

之后分享个心得:刚开始接触Promise会晕,多写几个例子就明白了。从封装简单的setTimeout开始,慢慢过渡到真实API请求,你会发现Promise实挺简单。

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