Skip to content

Promise 对象

Promise 是 JavaScript 中处理异步操作的重要对象。它代表了一个异步操作的最终完成(或失败)及其结果值。Promise 提供了一种更优雅的方式来处理异步代码,避免了回调地狱的问题。

什么是 Promise?

Promise 是一个代表异步操作最终完成或失败的对象。它有三种状态:

  1. Pending(待定):初始状态,既没有被兑现,也没有被拒绝
  2. Fulfilled(已兑现):意味着操作成功完成
  3. Rejected(已拒绝):意味着操作失败

创建 Promise

使用 Promise 构造函数可以创建一个新的 Promise 对象:

javascript
// 创建一个 Promise
let myPromise = new Promise((resolve, reject) => {
    // 异步操作
    setTimeout(() => {
        let success = true;
        if (success) {
            resolve("操作成功!");
        } else {
            reject("操作失败!");
        }
    }, 1000);
});

// 使用 Promise
myPromise
    .then(result => {
        console.log(result); // "操作成功!"
    })
    .catch(error => {
        console.error(error); // "操作失败!"
    });

Promise 方法

then 方法

then 方法用于处理 Promise 成功兑现的情况:

javascript
let promise = new Promise((resolve, reject) => {
    setTimeout(() => resolve("成功!"), 1000);
});

promise.then(
    result => {
        console.log(result); // "成功!"
        // 可以返回新的值
        return result.toUpperCase();
    }
).then(
    result => {
        console.log(result); // "成功!"
    }
);

catch 方法

catch 方法用于处理 Promise 被拒绝的情况:

javascript
let promise = new Promise((resolve, reject) => {
    setTimeout(() => reject(new Error("出错了!")), 1000);
});

promise
    .then(result => {
        console.log(result);
    })
    .catch(error => {
        console.error(error.message); // "出错了!"
    });

finally 方法

finally 方法无论 Promise 成功还是失败都会执行:

javascript
let promise = new Promise((resolve, reject) => {
    setTimeout(() => resolve("完成"), 1000);
});

promise
    .then(result => {
        console.log(result);
    })
    .catch(error => {
        console.error(error);
    })
    .finally(() => {
        console.log("清理工作完成");
    });

Promise 静态方法

Promise.resolve()

返回一个以给定值解析的 Promise 对象:

javascript
let promise = Promise.resolve("成功");

promise.then(result => {
    console.log(result); // "成功"
});

Promise.reject()

返回一个带有拒绝原因的 Promise 对象:

javascript
let promise = Promise.reject(new Error("失败"));

promise.catch(error => {
    console.error(error.message); // "失败"
});

Promise.all()

等待所有 Promise 完成,如果任何一个被拒绝,则返回第一个拒绝的 Promise:

javascript
let promise1 = Promise.resolve(3);
let promise2 = 42;
let promise3 = new Promise((resolve, reject) => {
    setTimeout(resolve, 100, 'foo');
});

Promise.all([promise1, promise2, promise3]).then((values) => {
    console.log(values); // [3, 42, "foo"]
});

Promise.allSettled()

等待所有 Promise 完成,无论成功还是失败:

javascript
let promise1 = Promise.resolve(3);
let promise2 = Promise.reject("错误");
let promise3 = new Promise((resolve, reject) => {
    setTimeout(resolve, 100, 'foo');
});

Promise.allSettled([promise1, promise2, promise3]).then((results) => {
    console.log(results);
    // [
    //   { status: 'fulfilled', value: 3 },
    //   { status: 'rejected', reason: '错误' },
    //   { status: 'fulfilled', value: 'foo' }
    // ]
});

Promise.race()

返回第一个完成的 Promise 的结果:

javascript
let promise1 = new Promise((resolve) => {
    setTimeout(resolve, 500, '一个');
});

let promise2 = new Promise((resolve) => {
    setTimeout(resolve, 100, '两个');
});

Promise.race([promise1, promise2]).then((value) => {
    console.log(value); // "两个"
});

链式调用

Promise 支持链式调用,可以依次处理多个异步操作:

javascript
function fetchUserData(userId) {
    return fetch(`/api/users/${userId}`)
        .then(response => response.json())
        .then(user => {
            console.log('获取用户信息:', user);
            return user;
        });
}

function fetchUserPosts(userId) {
    return fetch(`/api/users/${userId}/posts`)
        .then(response => response.json())
        .then(posts => {
            console.log('获取用户文章:', posts);
            return posts;
        });
}

// 链式调用
fetchUserData(123)
    .then(user => {
        return fetchUserPosts(user.id);
    })
    .then(posts => {
        console.log('处理文章数据:', posts);
    })
    .catch(error => {
        console.error('处理过程中出错:', error);
    });

错误处理

Promise 中的错误处理非常重要:

javascript
// 错误冒泡
new Promise((resolve, reject) => {
    resolve("成功");
})
.then(result => {
    throw new Error("处理过程中出错");
})
.then(result => {
    console.log("这不会被执行");
})
.catch(error => {
    console.error(error.message); // "处理过程中出错"
});

// 每个 Promise 都可以有自己的错误处理
new Promise((resolve, reject) => {
    setTimeout(() => reject("失败"), 1000);
})
.catch(error => {
    console.error("第一个错误处理:", error);
    // 可以返回值继续链式调用
    return "默认值";
})
.then(result => {
    console.log("恢复后的结果:", result); // "默认值"
});

实际应用示例

加载图片

javascript
function loadImage(src) {
    return new Promise((resolve, reject) => {
        let img = new Image();
        img.onload = () => resolve(img);
        img.onerror = () => reject(new Error(`图片加载失败: ${src}`));
        img.src = src;
    });
}

loadImage('path/to/image.jpg')
    .then(img => {
        document.body.appendChild(img);
        console.log('图片加载成功');
    })
    .catch(error => {
        console.error(error.message);
    });

并行处理多个请求

javascript
function fetchUserDetails(userId) {
    return Promise.all([
        fetch(`/api/users/${userId}`).then(r => r.json()),
        fetch(`/api/users/${userId}/posts`).then(r => r.json()),
        fetch(`/api/users/${userId}/comments`).then(r => r.json())
    ]).then(([user, posts, comments]) => {
        return {
            user,
            posts,
            comments
        };
    });
}

fetchUserDetails(123)
    .then(data => {
        console.log('用户详情:', data);
    })
    .catch(error => {
        console.error('获取用户详情失败:', error);
    });

与 async/await 结合使用

Promise 与 async/await 结合使用可以让异步代码看起来像同步代码:

javascript
async function fetchUserData(userId) {
    try {
        let userResponse = await fetch(`/api/users/${userId}`);
        let user = await userResponse.json();
        
        let postsResponse = await fetch(`/api/users/${userId}/posts`);
        let posts = await postsResponse.json();
        
        return { user, posts };
    } catch (error) {
        console.error('获取数据失败:', error);
        throw error;
    }
}

最佳实践

  1. 总是处理错误:使用 .catch() 或 try/catch 处理 Promise 错误。

  2. 避免嵌套 Promise:使用链式调用而不是嵌套。

javascript
// 不好的做法
promise.then(result => {
    return anotherPromise(result).then(anotherResult => {
        return yetAnotherPromise(anotherResult);
    });
});

// 好的做法
promise
    .then(result => anotherPromise(result))
    .then(anotherResult => yetAnotherPromise(anotherResult));
  1. 合理使用 Promise.all:当需要并行执行多个不相关的异步操作时使用。

  2. 避免在 Promise 构造函数中使用 Promise:这会导致不必要的嵌套。

javascript
// 不好的做法
new Promise((resolve, reject) => {
    somePromise().then(resolve, reject);
});

// 好的做法
somePromise();
  1. 使用 Promise.allSettled 处理部分失败:当希望即使部分 Promise 失败也要处理成功的 Promise 时使用。

Promise 是现代 JavaScript 异步编程的核心概念,掌握它对于编写高质量的异步代码至关重要。