promise 是ES6的一个内置对象 通常用来解决异步调用的问题 解决多层回调嵌套的问题 提高代码的可读性
Promise 的核心是一个状态机,它有且仅有三种状态:
关键特性:不可逆性 状态只能从 Pending 变为 Fulfilled 或 Rejected。一旦状态改变,就不会再变,任何时候都可以得到这个结果
Promise 的构造函数体是同步执行的。
当你 new Promise 的那一刻,执行器内部的代码会立即在当前主线程执行,并不会进入微任务队列
resolve()/reject() 本身是同步调用,但它们触发的 .then() / .catch() 回调是异步(微任务)
用于并行处理多个promise,可以同时执行多个异步操作(同时执行,而非顺序执行),并在所有操作完成后获取结果
参数,是多个promise实例
成功的条件是所有的promise成功,返回包含所有结果的数组,结果顺序固定,与传入的promise顺序一致
任何一个 Promise 失败,立即返回第一个失败的错误信息
Promise.all 适用于逻辑耦合的场景。如果任务 A 和任务 B 缺一不可才能渲染页面,就用它
并行处理多个promise,并在所有promise完成后返回包含每个promise结果的数组
Promise.allSettled 适用于任务相互独立且需要获取每个任务最终状态的情况。即使有些请求挂了,也不影响你展示其他成功的数据
接受一个promise数组,返回第一个完成的promise,无论成功还是失败
特点: 典型的超时机制实现工具
返回第一个成功的promise的值,全部失败的时候,返回错误信息
特点:高可用性。只有当所有人都不行了(AggregateError),它才放弃
当你执行 promise.then(fn) 且此时状态为 PENDING 时,Promise 并没有执行 fn,而是把 fn 存入(push) 内部的数组中。
当异步操作结束,你调用 resolve(value)。此时,Promise 内部会遍历数组,把之前存入的所有 fn 拿出来依次执行。
// 定义三种状态
const PENDING = 'pending';
const FULFILLED = 'fulfilled';
const REJECTED = 'rejected';
class MyPromise {
constructor(executor) {
this.status = PENDING; // 初始状态
this.value = undefined; // 成功的值
this.reason = undefined; // 失败的原因
// 成功和失败的回调队列(用于处理异步 PENDING 时的 then)
this.onResolvedCallbacks = [];
this.onRejectedCallbacks = [];
// 内部的 resolve 工具函数
const resolve = (value) => {
if (this.status === PENDING) {
this.status = FULFILLED;
this.value = value;
// 瞬间引爆:执行所有收集到的成功回调
this.onResolvedCallbacks.forEach(fn => fn());
}
};
// 内部的 reject 工具函数
const reject = (reason) => {
if (this.status === PENDING) {
this.status = REJECTED;
this.reason = reason;
// 瞬间引爆:执行所有收集到的失败回调
this.onRejectedCallbacks.forEach(fn => fn());
}
};
// 对应你说的:try-catch 保证构造函数安全
try {
executor(resolve, reject);
} catch (err) {
reject(err);
}
}
then(onFulfilled, onRejected) {
// 规范:参数可选,实现值穿透
onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : v => v;
onRejected = typeof onRejected === 'function' ? onRejected : err => { throw err };
// 为了链式调用,必须返回一个新的 Promise 实例
const promise2 = new MyPromise((resolve, reject) => {
// 封装执行逻辑并放入微任务队列
const fulfilledMicrotask = () => {
queueMicrotask(() => { // ⭐️ 关键:包装为异步微任务
try {
const x = onFulfilled(this.value);
// 递归解析 x 的值(处理 x 也是 Promise 的情况)
resolvePromise(promise2, x, resolve, reject);
} catch (e) {
reject(e);
}
});
};
const rejectedMicrotask = () => {
queueMicrotask(() => { // ⭐️ 关键:包装为异步微任务
try {
const x = onRejected(this.reason);
resolvePromise(promise2, x, resolve, reject);
} catch (e) {
reject(e);
}
});
};
// 状态机判断
if (this.status === FULFILLED) {
fulfilledMicrotask();
} else if (this.status === REJECTED) {
rejectedMicrotask();
} else if (this.status === PENDING) {
// 对应你说的:异步时先 push 到队列
this.onResolvedCallbacks.push(fulfilledMicrotask);
this.onRejectedCallbacks.push(rejectedMicrotask);
}
});
return promise2;
}
}async/await 是 Promise 的语法糖,它的本质是 Promise + 生成器(Generator)的组合,让异步代码可以用同步的方式书写。
// 情况1:返回普通值
async function fn1() {
return 42;
}
// 等价于:return Promise.resolve(42)
// 情况2:返回 Promise
async function fn2() {
return Promise.resolve(42);
}
// 直接返回这个 Promise(不会二次包装)
// 情况3:抛出错误
async function fn3() {
throw new Error('出错了');
}
// 等价于:return Promise.reject(new Error('出错了'))
// 情况4:没有 return
async function fn4() {
console.log('执行');
}
// 等价于:return Promise.resolve(undefined)await 实际上是一个 “代码切割机”。当你写下 const res = await p1; 时,引擎会把你的函数从这里切成两半:
await 后面的代码被放进微任务队列的时机,只取决于 await 后面表达式的 Promise 何时被 resolve
await 后面的任何值,都会被 Promise.resolve() 包装成一个 Promise
| 场景 | resolve() 调用时机 | await 后面代码入队时机 |
|---|---|---|
| Promise 内部全是同步代码 | 立即 | 立即入微任务 |
| Promise 内部有 setTimeout(0ms) | 下一个宏任务 | 下一个宏任务执行时才入微任务 |
| Promise 内部有 100 层嵌套异步 | 最内层 resolve 时 | 最内层 resolve 时才入微任务 |
| Promise 永远不 resolve | 永不 | 永不入队,代码永远不执行 |
✅ await 只阻塞它后面的代码(在同一个 async 函数内)
✅ 把后面的代码包装成微任务后,立即让出主线程
✅ 继续执行 async 函数外面的同步代码和已有任务
用 Promise:当你需要并发处理时。比如 Promise.all([p1, p2])。
用 async/await:当你的异步任务有严格的先后顺序(串行)时。
底层原理:永远记住,await 下面的代码就是 .then() 里的微任务
