文章目录
-
- [一、Promise 出现的背景:解决回调地狱](#一、Promise 出现的背景:解决回调地狱)
-
- [1. 为什么需要 Promise?](#1. 为什么需要 Promise?)
- [2. Promise 核心作用](#2. Promise 核心作用)
- [二、Promise 基础核心知识点(必背)](#二、Promise 基础核心知识点(必背))
-
- [1. 三种状态(不可逆)](#1. 三种状态(不可逆))
- [2. 执行规则:同步 + 微任务](#2. 执行规则:同步 + 微任务)
- [3. 核心 API 速查](#3. 核心 API 速查)
- [三、Promise 原理:手写 Promise(核心实现)](#三、Promise 原理:手写 Promise(核心实现))
- 四、面试必考:经典输出题(逐行解析)
-
- [题 0:经典综合题](#题 0:经典综合题)
- [五、Promise 高频输出题合集(面试必刷)](#五、Promise 高频输出题合集(面试必刷))
-
- [题 1:基础链式调用](#题 1:基础链式调用)
- [题 2:catch 穿透](#题 2:catch 穿透)
- [题 3:finally 特性](#题 3:finally 特性)
- [题 4:值穿透(经典陷阱)](#题 4:值穿透(经典陷阱))
- [题 5:返回 Promise 的 then](#题 5:返回 Promise 的 then)
- [题 6:微任务嵌套(刁钻)](#题 6:微任务嵌套(刁钻))
- [题 7:多个微任务交替(高难度)](#题 7:多个微任务交替(高难度))
- [题 8:async/await 与 Promise 混用(超高频)](#题 8:async/await 与 Promise 混用(超高频))
- [题 9:Promise.all 与错误处理](#题 9:Promise.all 与错误处理)
- [题 10:resolve 传入 Promise(超刁钻)](#题 10:resolve 传入 Promise(超刁钻))
- [题 11:finally 的返回值陷阱](#题 11:finally 的返回值陷阱)
- [题 12:多层 catch 后的状态](#题 12:多层 catch 后的状态)
- [题 13:同步抛错 vs 异步抛错](#题 13:同步抛错 vs 异步抛错)
- [题 14:Promise.race 竞速场景](#题 14:Promise.race 竞速场景)
- [题 15:地狱级综合题(模拟真实面试)](#题 15:地狱级综合题(模拟真实面试))
- [六、手撕 Promise 核心 API(面试高频手写)](#六、手撕 Promise 核心 API(面试高频手写))
-
- [1. 手撕 Promise.all(重点)](#1. 手撕 Promise.all(重点))
- [2. 手撕 Promise.race](#2. 手撕 Promise.race)
- [3. 手撕 Promise.allSettled](#3. 手撕 Promise.allSettled)
- [4. 手撕 Promise.any](#4. 手撕 Promise.any)
- [5. 手撕 Promise 并发限制(加分题)](#5. 手撕 Promise 并发限制(加分题))
- [6. 手撕 Promise 重试机制(加分题)](#6. 手撕 Promise 重试机制(加分题))
- [七、Promise 高频面试问答(背会直接用)](#七、Promise 高频面试问答(背会直接用))
-
- [Q1:Promise 和 async/await 的关系?](#Q1:Promise 和 async/await 的关系?)
- [Q2:then 为什么可以链式调用?](#Q2:then 为什么可以链式调用?)
- [Q3:Promise 错误捕获方式有哪些?](#Q3:Promise 错误捕获方式有哪些?)
- [Q4:all 和 allSettled 的区别?](#Q4:all 和 allSettled 的区别?)
- [Q5:Promise 的错误为什么不能被 try/catch 捕获?](#Q5:Promise 的错误为什么不能被 try/catch 捕获?)
- [Q6:如何取消一个 Promise?](#Q6:如何取消一个 Promise?)
- [Q7:Promise.resolve() 和 new Promise(resolve => resolve()) 有什么区别?](#Q7:Promise.resolve() 和 new Promise(resolve => resolve()) 有什么区别?)
- Q8:微任务和宏任务的完整执行模型?
- 八、总结速查表
本文覆盖所有 Promise 面试考点:诞生背景、基础用法、微任务执行机制、经典输出题(含刁钻变体)、手撕核心 API(all / race / allSettled / any)及原理剖析。背完就能搞定 99% 的 Promise 面试。
一、Promise 出现的背景:解决回调地狱
1. 为什么需要 Promise?
早期异步代码用回调函数实现,多层嵌套会形成回调地狱(Callback Hell),代码可读性极差、难以维护:
js
// 回调地狱:嵌套多层,逻辑混乱,错误处理更是噩梦
ajax('url1', (res1) => {
ajax('url2', (res2) => {
ajax('url3', (res3) => {
// 如果这里报错,外层 try/catch 无法捕获
console.log(res3)
}, err => console.log(err))
}, err => console.log(err))
}, err => console.log(err))
核心痛点:
- 嵌套层级深,逻辑难以理解
- 每一层都要单独处理错误
- 无法优雅地并行 / 串行控制多个异步任务
2. Promise 核心作用
- 把异步回调嵌套改成链式调用,代码扁平化
- 统一处理异步成功 / 失败,支持并行 / 串行控制
- 是 JS 异步编程的基石(
async/await是语法糖,底层还是 Promise)
二、Promise 基础核心知识点(必背)
1. 三种状态(不可逆)
| 状态 | 触发条件 | 是否可逆 |
|---|---|---|
pending(等待中) |
初始状态 | --- |
fulfilled(已成功) |
调用 resolve() 后 |
❌ 永久锁定 |
rejected(已失败) |
调用 reject() 后 |
❌ 永久锁定 |
状态一旦改变,永久锁定,无法再次修改。
js
const p = new Promise((resolve, reject) => {
resolve('first') // 状态变为 fulfilled
reject('second') // 无效!状态已锁定
resolve('third') // 无效!状态已锁定
})
p.then(res => console.log(res)) // 只输出 'first'
2. 执行规则:同步 + 微任务
- Promise 构造函数内的代码是同步执行的
.then()/.catch()/.finally()是微任务,会进入微任务队列,等同步代码执行完才执行- 执行顺序:同步代码 → 微任务 → 宏任务(setTimeout、setInterval、ajax 等)
js
console.log('1 - 同步')
new Promise((resolve) => {
console.log('2 - 构造函数内,同步')
resolve()
}).then(() => {
console.log('4 - 微任务')
})
setTimeout(() => {
console.log('5 - 宏任务')
}, 0)
console.log('3 - 同步')
// 输出:1 2 3 4 5
3. 核心 API 速查
实例方法:
| 方法 | 作用 |
|---|---|
.then(onFulfilled, onRejected) |
成功回调(第二参数可选,处理失败) |
.catch(onRejected) |
失败回调,等价于 .then(null, onRejected) |
.finally(callback) |
无论成败都执行,不接收参数,不改变状态 |
静态方法:
| 方法 | 作用 |
|---|---|
Promise.resolve(value) |
返回一个成功状态的 Promise |
Promise.reject(reason) |
返回一个失败状态的 Promise |
Promise.all(iterable) |
全部成功才成功,有一个失败即失败 |
Promise.race(iterable) |
第一个完成的(无论成败)决定结果 |
Promise.allSettled(iterable) |
全部完成后返回所有结果(含成败状态) |
Promise.any(iterable) |
第一个成功的决定结果,全部失败才失败 |
三、Promise 原理:手写 Promise(核心实现)
想真正理解 Promise,必须手写过一次。以下是符合 Promise/A+ 规范的核心实现:
js
class MyPromise {
// 状态常量
static PENDING = 'pending'
static FULFILLED = 'fulfilled'
static REJECTED = 'rejected'
constructor(executor) {
this.status = MyPromise.PENDING
this.value = undefined
this.reason = undefined
this.onFulfilledCallbacks = []
this.onRejectedCallbacks = []
const resolve = (value) => {
// 只有 pending 状态才能转换
if (this.status === MyPromise.PENDING) {
this.status = MyPromise.FULFILLED
this.value = value
this.onFulfilledCallbacks.forEach(fn => fn(value))
}
}
const reject = (reason) => {
if (this.status === MyPromise.PENDING) {
this.status = MyPromise.REJECTED
this.reason = reason
this.onRejectedCallbacks.forEach(fn => fn(reason))
}
}
try {
executor(resolve, reject)
} catch (e) {
reject(e)
}
}
then(onFulfilled, onRejected) {
// 值穿透:若不是函数,默认透传
onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : v => v
onRejected = typeof onRejected === 'function' ? onRejected : e => { throw e }
// then 返回一个新的 Promise(链式调用的关键)
return new MyPromise((resolve, reject) => {
const handleFulfilled = () => {
queueMicrotask(() => {
try {
const result = onFulfilled(this.value)
resolvePromise(result, resolve, reject)
} catch (e) {
reject(e)
}
})
}
const handleRejected = () => {
queueMicrotask(() => {
try {
const result = onRejected(this.reason)
resolvePromise(result, resolve, reject)
} catch (e) {
reject(e)
}
})
}
if (this.status === MyPromise.FULFILLED) handleFulfilled()
else if (this.status === MyPromise.REJECTED) handleRejected()
else {
// pending 状态:异步,先收集回调
this.onFulfilledCallbacks.push(handleFulfilled)
this.onRejectedCallbacks.push(handleRejected)
}
})
}
catch(onRejected) {
return this.then(null, onRejected)
}
finally(callback) {
return this.then(
value => MyPromise.resolve(callback()).then(() => value),
reason => MyPromise.resolve(callback()).then(() => { throw reason })
)
}
static resolve(value) {
if (value instanceof MyPromise) return value
return new MyPromise(resolve => resolve(value))
}
static reject(reason) {
return new MyPromise((_, reject) => reject(reason))
}
}
// 处理 then 返回值(Promise/A+ 2.3 规范)
function resolvePromise(result, resolve, reject) {
if (result instanceof MyPromise) {
result.then(resolve, reject)
} else {
resolve(result)
}
}
四、面试必考:经典输出题(逐行解析)
题 0:经典综合题
js
console.log(5)
try {
new Promise((resolve, reject) => {
console.log(3) // 构造函数内:同步执行
reject('err') // 直接把 Promise 状态改为 rejected
}).catch(() => {
console.log(2) // 微任务1:捕获失败,执行
}).then(() => {
console.log(9) // 微任务2:catch执行完,返回新Promise成功,执行
})
console.log(10) // 同步代码
} catch (e) {
console.log(11) // Promise的错误是微任务,不会触发try/catch
}
执行步骤:
- 同步执行
console.log(5)→ 输出 5 - 进入
try,执行 Promise 构造函数(同步)→console.log(3)→ 输出 3 - 执行
reject('err'),Promise 状态变为rejected,注册.catch()微任务 - 继续执行同步代码
console.log(10)→ 输出 10 - 同步代码执行完毕,开始执行微任务队列
- 执行
.catch()→console.log(2)→ 输出 2 .catch()返回一个新的成功状态 Promise → 执行后续.then()→console.log(9)→ 输出 9- Promise 错误是微任务,不会触发同步的
try/catch→ 不输出 11
最终输出:5 3 10 2 9
五、Promise 高频输出题合集(面试必刷)
题 1:基础链式调用
js
Promise.resolve()
.then(() => { console.log(1) })
.then(() => { console.log(2) })
console.log(3)
输出:3 1 2
console.log(3)是同步代码,先执行;.then()是微任务,同步执行完后依次执行。
题 2:catch 穿透
js
new Promise((res, rej) => {
rej('err')
}).then(() => {
console.log(1)
}).then(() => {
console.log(2)
}).catch(() => {
console.log(3)
})
输出:3
rejected状态会穿透所有.then()(没有onRejected参数时),直到第一个.catch()。
题 3:finally 特性
js
Promise.resolve('ok')
.finally(() => console.log(1))
.then(res => console.log(res))
输出:1 ok
finally不接收参数,不改变 Promise 状态,透传'ok'。
题 4:值穿透(经典陷阱)
js
Promise.resolve(1)
.then(2)
.then(Promise.resolve(3))
.then(console.log)
输出:1
.then()的参数如果不是函数,会被忽略,发生值穿透 (透传上一个 Promise 的值)。2和Promise.resolve(3)都不是函数,所以最终透传初始值1。
题 5:返回 Promise 的 then
js
Promise.resolve()
.then(() => {
return new Promise(resolve => {
setTimeout(() => resolve(1), 0)
})
})
.then(res => console.log(res))
console.log(2)
输出:2 1
第一个
.then()返回了一个 Promise,第二个.then()会等待这个内部 Promise resolve 后才执行,因此延迟到 setTimeout 之后。
题 6:微任务嵌套(刁钻)
js
Promise.resolve().then(() => {
console.log(1)
Promise.resolve().then(() => {
console.log(2)
})
}).then(() => {
console.log(3)
})
输出:1 2 3
第一个
.then()执行后,内部Promise.resolve().then(()=>console.log(2))将2的微任务加入队列,随后外层第二个.then(console.log(3))也加入队列。微任务队列:[打印2, 打印3],依次执行。
题 7:多个微任务交替(高难度)
js
Promise.resolve().then(() => {
console.log(1)
return Promise.resolve(5)
}).then(v => {
console.log(v)
})
Promise.resolve().then(() => {
console.log(2)
}).then(() => {
console.log(3)
}).then(() => {
console.log(4)
})
输出:1 2 3 4 5
这是最容易答错的题目,关键在于
return Promise.resolve(5)会多消耗 2 个微任务 tick。
详细分析(按微任务队列轮次):
- 第1轮微任务 :执行
then(()=>console.log(1)...),输出 1 ;执行then(()=>console.log(2)),输出 2 - 第一个链返回了
Promise.resolve(5),需要额外 2 次 tick 才能将5传递给下一个.then() - 第2轮微任务 :第二链执行
then(()=>console.log(3)),输出 3 ;第一链内部处理Promise.resolve(5)(第1个额外 tick) - 第3轮微任务 :第二链执行
then(()=>console.log(4)),输出 4 ;第一链内部处理(第2个额外 tick),将5放入队列 - 第4轮微任务 :第一链执行
.then(v=>console.log(v)),输出 5
题 8:async/await 与 Promise 混用(超高频)
js
async function async1() {
console.log('async1 start')
await async2()
console.log('async1 end')
}
async function async2() {
console.log('async2')
}
console.log('script start')
setTimeout(() => console.log('setTimeout'), 0)
async1()
new Promise(resolve => {
console.log('promise1')
resolve()
}).then(() => {
console.log('promise2')
})
console.log('script end')
输出:
script start
async1 start
async2
promise1
script end
async1 end
promise2
setTimeout
解析:
'script start'同步输出setTimeout注册宏任务- 调用
async1():同步输出'async1 start',调用async2() async2()同步输出'async2',返回Promise.resolve(undefined)await等待async2()resolve,'async1 end'后面的代码注册为微任务- 回到主线程,执行
new Promise构造函数,同步输出'promise1',resolve()后注册.then()微任务 - 同步输出
'script end' - 执行微任务:
'async1 end',然后'promise2' - 执行宏任务:
'setTimeout'
题 9:Promise.all 与错误处理
js
const p1 = new Promise((resolve) => setTimeout(() => resolve(1), 100))
const p2 = new Promise((_, reject) => setTimeout(() => reject('err'), 50))
const p3 = new Promise((resolve) => setTimeout(() => resolve(3), 200))
Promise.all([p1, p2, p3])
.then(res => console.log('all:', res))
.catch(err => console.log('catch:', err))
输出:catch: err
p2在 50ms 时失败,Promise.all立即reject,不等p1、p3。
题 10:resolve 传入 Promise(超刁钻)
js
const p1 = new Promise(resolve => {
resolve(Promise.resolve('hello'))
})
p1.then(res => console.log(res))
const p2 = new Promise(resolve => {
resolve('world')
})
p2.then(res => console.log(res))
输出:world hello
resolve接收的是一个 Promise 时,会等待该 Promise 完成,额外消耗 2 个微任务 tick,所以p2.then先于p1.then执行。
题 11:finally 的返回值陷阱
js
Promise.resolve('ok')
.finally(() => {
return Promise.reject('error in finally')
})
.then(res => console.log('then:', res))
.catch(err => console.log('catch:', err))
输出:catch: error in finally
finally内部如果抛出错误或return一个rejected Promise,会改变最终状态为 rejected ,这是finally唯一能影响状态的情况。
题 12:多层 catch 后的状态
js
Promise.reject('err')
.catch(err => {
console.log(1, err)
return 'recovered'
})
.catch(err => {
console.log(2, err)
})
.then(res => {
console.log(3, res)
})
输出:1 'err' → 3 'recovered'
第一个
.catch()处理了错误并return 'recovered'(正常返回),后续状态变为fulfilled,第二个.catch()不执行,.then()接收到'recovered'。
题 13:同步抛错 vs 异步抛错
js
// 情况A:同步抛错(会被 Promise 捕获)
new Promise((resolve, reject) => {
throw new Error('sync error')
}).catch(err => console.log('A:', err.message))
// 情况B:异步抛错(无法被 Promise 捕获!)
new Promise((resolve, reject) => {
setTimeout(() => {
throw new Error('async error') // ❌ 无法捕获,直接报 unhandledrejection
}, 0)
}).catch(err => console.log('B:', err.message))
输出:A: sync error(情况B的错误无法被捕获,直接抛到全局)
Promise 只能捕获同步抛出 的错误或通过
reject()传递的错误,异步回调内的抛错已脱离 Promise 的执行上下文。
题 14:Promise.race 竞速场景
js
function timeout(ms) {
return new Promise((_, reject) =>
setTimeout(() => reject(new Error('timeout')), ms)
)
}
Promise.race([
fetch('/api/data'), // 假设需要 3000ms
timeout(2000)
]).then(res => console.log('success'))
.catch(err => console.log('error:', err.message))
输出:error: timeout(2000ms 超时后)
实际项目中
Promise.race常用于请求超时控制。
题 15:地狱级综合题(模拟真实面试)
js
console.log(1)
setTimeout(() => console.log(2), 0)
new Promise((resolve) => {
console.log(3)
resolve()
console.log(4)
}).then(() => {
console.log(5)
new Promise(resolve => resolve()).then(() => {
console.log(6)
})
}).then(() => {
console.log(7)
})
new Promise((resolve) => {
console.log(8)
resolve()
}).then(() => {
console.log(9)
}).then(() => {
console.log(10)
})
console.log(11)
输出:1 3 4 8 11 5 9 6 7 10 2
逐步分析:
- 同步 :
1、3(构造函数)、4(resolve 后继续同步)、8(第二个构造函数)、11 - 微任务第1轮 :
5(第一个Promise的then1)、9(第二个Promise的then1)- then1 执行时注册了内部的
Promise.resolve().then(=>6)(微任务)和外层then2(=>7)(微任务),以及then2(=>10)
- then1 执行时注册了内部的
- 微任务第2轮 :
6(内部 then)、7(外层 then2)、10(第二个Promise的then2) - 宏任务 :
2
六、手撕 Promise 核心 API(面试高频手写)
1. 手撕 Promise.all(重点)
作用: 所有 Promise 成功才返回结果数组(顺序与输入一致),有一个失败直接 reject
js
Promise.myAll = function (promises) {
return new Promise((resolve, reject) => {
const result = []
let count = 0
// 空数组直接返回
if (promises.length === 0) return resolve(result)
promises.forEach((p, index) => {
// 包装成 Promise,兼容普通值
Promise.resolve(p).then(res => {
result[index] = res // 用 index 保证顺序,不能用 push
count++
if (count === promises.length) resolve(result)
}).catch(reject) // 有一个失败直接 reject
})
})
}
易错点: 用 result[index] = res 而不是 result.push(res),因为要保证结果顺序与入参一致。
2. 手撕 Promise.race
作用: 返回第一个完成的 Promise(无论成功 / 失败)
js
Promise.myRace = function (promises) {
return new Promise((resolve, reject) => {
promises.forEach(p => {
Promise.resolve(p).then(resolve).catch(reject)
})
})
}
原理: resolve / reject 只有第一次调用生效(状态锁定),后续调用无效。
3. 手撕 Promise.allSettled
作用: 所有 Promise 完成后(无论成败)返回结果数组,每项包含 status + value/reason
js
Promise.myAllSettled = function (promises) {
return new Promise(resolve => {
const result = []
let count = 0
if (promises.length === 0) return resolve(result)
promises.forEach((p, index) => {
Promise.resolve(p)
.then(res => {
result[index] = { status: 'fulfilled', value: res }
})
.catch(err => {
result[index] = { status: 'rejected', reason: err }
})
.finally(() => {
count++
if (count === promises.length) resolve(result)
})
})
})
}
4. 手撕 Promise.any
作用: 返回第一个成功的 Promise,全部失败才报 AggregateError
js
Promise.myAny = function (promises) {
return new Promise((resolve, reject) => {
let count = 0
const errors = []
if (promises.length === 0) {
return reject(new AggregateError([], 'All promises were rejected'))
}
promises.forEach((p, index) => {
Promise.resolve(p).then(resolve).catch(err => {
errors[index] = err
count++
if (count === promises.length) {
reject(new AggregateError(errors, 'All promises were rejected'))
}
})
})
})
}
5. 手撕 Promise 并发限制(加分题)
场景: 有 100 个请求,最多同时发起 3 个
js
async function limitConcurrency(tasks, limit) {
const result = []
const executing = new Set()
for (const [index, task] of tasks.entries()) {
const p = Promise.resolve().then(() => task()).then(res => {
result[index] = res
executing.delete(p)
})
executing.add(p)
if (executing.size >= limit) {
// 等待其中最快完成的那个
await Promise.race(executing)
}
}
await Promise.all(executing)
return result
}
// 用法
const tasks = Array.from({ length: 10 }, (_, i) =>
() => new Promise(resolve => setTimeout(() => resolve(i), Math.random() * 1000))
)
limitConcurrency(tasks, 3).then(console.log)
6. 手撕 Promise 重试机制(加分题)
js
function retry(fn, times, delay = 0) {
return new Promise((resolve, reject) => {
function attempt(remaining) {
fn()
.then(resolve)
.catch(err => {
if (remaining <= 0) return reject(err)
setTimeout(() => attempt(remaining - 1), delay)
})
}
attempt(times)
})
}
// 用法:最多重试3次,每次间隔1秒
retry(() => fetch('/api'), 3, 1000)
.then(res => console.log('success'))
.catch(err => console.log('failed after 3 retries'))
七、Promise 高频面试问答(背会直接用)
Q1:Promise 和 async/await 的关系?
async/await 是 Promise 的语法糖 ,底层还是基于 Promise 实现,让异步代码写法更像同步代码,同时使 try/catch 可以正常捕获异步错误。
js
// 等价关系
async function fn() {
const res = await somePromise
return res
}
// 等价于
function fn() {
return somePromise.then(res => res)
}
Q2:then 为什么可以链式调用?
因为 .then() / .catch() 每次都会返回一个新的 Promise,所以可以链式调用。这个新 Promise 的状态由回调函数的返回值决定:
- 返回普通值 → 新 Promise fulfilled
- 返回 Promise → 新 Promise 等待该 Promise
- 抛出错误 → 新 Promise rejected
Q3:Promise 错误捕获方式有哪些?
.catch()捕获所有前面链上的错误(穿透特性).then(null, onRejected)只捕获当前链节点的错误try/catch配合async/await使用- 全局监听:
window.addEventListener('unhandledrejection', handler)
Q4:all 和 allSettled 的区别?
| 特性 | Promise.all |
Promise.allSettled |
|---|---|---|
| 失败处理 | 一个失败,立即 reject(短路) | 等全部完成,收集所有结果 |
| 适用场景 | 所有任务必须成功 | 需要知道每个任务的结果 |
| 返回值 | 成功值数组 | {status, value/reason} 数组 |
Q5:Promise 的错误为什么不能被 try/catch 捕获?
js
try {
new Promise((_, reject) => {
reject('err') // 这是异步的微任务,脱离了 try/catch 的同步执行上下文
})
} catch (e) {
console.log(e) // ❌ 不会执行
}
reject() 触发的是微任务 ,执行时 try/catch 的同步代码已经执行完毕,所以无法捕获。只有同步抛出的错误才能被 try/catch 捕获。
Q6:如何取消一个 Promise?
Promise 本身不支持取消,但有两种模拟方式:
js
// 方式1:使用 AbortController(推荐,用于 fetch)
const controller = new AbortController()
fetch('/api', { signal: controller.signal })
controller.abort() // 取消请求
// 方式2:竞态 + 标志位
function cancellable(promise) {
let isCancelled = false
const wrapped = new Promise((resolve, reject) => {
promise.then(
val => !isCancelled && resolve(val),
err => !isCancelled && reject(err)
)
})
return {
promise: wrapped,
cancel() { isCancelled = true }
}
}
Q7:Promise.resolve() 和 new Promise(resolve => resolve()) 有什么区别?
js
// 如果传入的是普通值,完全等价
Promise.resolve(1)
// 等价于
new Promise(resolve => resolve(1))
// 如果传入的是 Promise,Promise.resolve 会直接返回该 Promise(同一个引用)
const p = Promise.resolve(new Promise(r => r(1)))
// p 就是传入的那个 Promise,而不是新创建的
Q8:微任务和宏任务的完整执行模型?
事件循环(Event Loop)执行顺序:
1. 执行当前宏任务(包括同步代码)
2. 清空微任务队列(所有微任务,包括微任务执行过程中新增的微任务)
3. 执行下一个宏任务
4. 重复 2-3
微任务:Promise.then/catch/finally、queueMicrotask、MutationObserver
宏任务:setTimeout、setInterval、setImmediate、I/O、UI rendering
八、总结速查表
| 考点 | 关键结论 |
|---|---|
| 执行顺序 | 构造函数同步 → then/catch 微任务 → 宏任务 |
| 状态不可逆 | pending → fulfilled/rejected,一旦修改无法回头 |
| 值穿透 | .then() 参数非函数时,透传上一个 Promise 的值 |
| catch 穿透 | rejected 状态跳过所有 .then(),直到第一个 .catch() |
| finally 特性 | 不接收参数、不改变状态(除非内部 reject/throw) |
| return Promise | 在 then 中 return 一个 Promise,会额外消耗 2 个微任务 tick |
| all vs race | all 全部成功,race 第一个完成 |
| all vs allSettled | all 短路,allSettled 等全部完成 |
| race vs any | race 第一个(含失败),any 第一个成功 |
| 错误捕获 | .catch() 捕获链上错误,try/catch 只能配合 async/await |
面试黄金法则: 看到输出题,先画微任务队列草图,标清每轮哪些微任务进队、哪些出队,一步步推导。