第一次接触 Promise 时,then、catch 和一串回调看起来很绕。其实顺着“异步操作什么时候有结果”这条线去理解,它并不复杂:Promise 负责保存未来的成功或失败结果,async/await 则让我们用更接近同步代码的方式读取这个结果。
一、Promise 的三种状态
Promise 有三种状态:
pending:操作还没有完成。fulfilled:操作成功,得到了结果。rejected:操作失败,得到了错误原因。
状态只能从 pending 变为 fulfilled 或 rejected。一旦确定,就不能再次改变。
const request = new Promise((resolve, reject) => {
setTimeout(() => {
const success = true;
success
? resolve('数据加载成功')
: reject(new Error('网络请求失败'));
}, 1000);
});
new Promise() 的执行器函数会立即同步执行,真正异步的是里面的定时器、网络请求等任务。resolve 和 reject 负责确定最终状态。
二、链式调用与错误传递
then 会返回一个新的 Promise,所以前一步的返回值可以直接交给下一步。
fetchUser()
.then(user => fetchOrders(user.id))
.then(orders => orders.filter(order => order.paid))
.then(paidOrders => console.log(paidOrders))
.catch(error => console.error('加载失败:', error))
.finally(() => console.log('请求结束'));
如果 then 返回普通值,下一步会接收到这个值;如果返回另一个 Promise,链条会等待它完成;如果抛出异常,错误会进入后面的 catch。常见问题是忘记 return:异步任务没有被返回时,后面的步骤就不会等待它。
三、多个异步任务怎么组合
处理多个任务时,根据业务能否接受部分失败来选择方法:
Promise.all:全部成功才返回,任意一个失败就整体失败。Promise.allSettled:等待全部结束,并保留每一项的成功或失败状态。Promise.any:返回第一个成功结果,全部失败才拒绝。Promise.race:返回第一个完成的结果,不区分成功或失败。
const results = await Promise.allSettled([
fetchProfile(),
fetchMessages(),
fetchNotifications()
]);
const successful = results
.filter(item => item.status === 'fulfilled')
.map(item => item.value);
例如首屏核心数据缺一不可时可以使用 Promise.all;多个独立模块分别加载时,Promise.allSettled 更适合保留部分成功结果。
四、Promise 与微任务
then、catch、finally 的回调属于微任务。当前同步代码执行完后,微任务会先于 setTimeout 这类宏任务执行。
console.log(1);
Promise.resolve().then(() => console.log(2));
setTimeout(() => console.log(3));
console.log(4);
// 1、4、2、3
这里先输出同步代码 1 和 4,再执行微任务输出 2,最后执行定时器输出 3。理解这个顺序,有助于排查请求回调、页面更新和定时器之间的问题。
五、async 和 await 分别做了什么
async 用来声明异步函数。被 async 修饰的函数一定会返回 Promise:函数直接返回的普通值会被包装成成功结果,函数内部抛出的异常则会变成失败结果。
async function getName() {
return 'Tess';
}
getName().then(name => console.log(name)); // Tess
await 用来等待一个 Promise 的结果。它会暂停当前 async 函数后续代码的执行,但不会阻塞浏览器主线程。Promise 成功时,await 得到成功值;Promise 失败时,它会抛出异常,所以通常需要配合 try...catch。
async function loadPage() {
try {
const user = await fetchUser();
const orders = await fetchOrders(user.id);
return orders;
} catch (error) {
console.error('加载失败:', error);
throw error;
}
}
await 通常只能写在 async 函数中(ES Module 顶层 await 除外)。还要注意,连续写多个 await 会让任务按顺序执行。如果多个请求互不依赖,可以先创建 Promise,再用 Promise.all 并行等待:
const [profile, messages] = await Promise.all([
fetchProfile(),
fetchMessages()
]);
所以,async 负责让函数返回 Promise,await 负责取出 Promise 的结果。它们让异步流程更容易阅读,但 Promise 的状态、错误传递和微任务规则仍然存在。