1. Promise完整实现
推导链
Q1:Promise是什么? → 状态机:pending → fulfilled / rejected,不可逆 → 需要:state、value、callback[]
Q2:resolve / reject 在干嘛? → 改状态 + 存值 + 执行所有等待的回调 → 为什么要callbacks?因为then可能在pending时调用
Q3:then为什么能链式调用? → 返回新Promise → 新Promise的resolve / reject 由上一个then的返回值决定
Q4:then的返回值怎么处理? → 普通值:直接resolve(rusult) → Promise:result.then(resolve, reject) 等它完成
Q5: 为什么要 setTimeout?
→ 规范要求 then 回调异步执行
Q6: 透传是什么?
→ .then().then(v => ...) 值要能传下去
→ 默认 v => v 和 e =>
代码
const PENDING = 'pending', FULFILLED = 'fulfilled', REJECTED = 'rejected'
class MyPromise {
constructor(executor) {
this.state = PENDING
this.value = undefined
this.callbacks = []
const resolve = (value) => {
if (this.state !== PENDING) return
this.state = FULFILLED
this.value = value
this.callbacks.forEach(cb => cb.onFulfilled(value))
}
const reject = (reason) => {
if (this.state !== PENDING) return
this.state = REJECTED
this.value = reason
this.callbacks.forEach(cb => cb.onRejected(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 }
return new MyPromise((resolve, reject) => {
const handle = (callback) => {
try {
const result = callback(this.value)
result instanceof MyPromise ? result.then(resolve, reject) : resolve(result)
} catch (e) { reject(e) }
}
if (this.state === FULFILLED) return setTimeout(() => handle(onFulfilled))
if (this.state === REJECTED) return setTimeout(() => handle(onRejected))
this.callbacks.push({
onFulfilled: () => setTimeout(() => handle(onFulfilled)),
onRejected: () => setTimeout(() => handle(onRejected))
})
})
}
catch(onRejected) { return this.then(null, onRejected) }
static resolve(v) { return v instanceof MyPromise ? v : new MyPromise(r => r(v)) }
static reject(e) { return new MyPromise((_, r) => r(e)) }
}
// 测试
new MyPromise(r => setTimeout(() => r(1), 100))
.then(v => { console.log(v); return v + 1 })
.then(v => new MyPromise(r => setTimeout(() => r(v + 1), 100)))
.then(console.log) // 1, 32. Promise.all
推导链
Q1: all 干嘛?→ 全部成功才成功,一个失败就失败
Q2: 怎么知道全部完成?→ 计数器 finished === total
Q3: 结果顺序?→ results[index] 按索引存
代码
Promise.myAll = function(promises) {
return new Promise((resolve, reject) => {
const results = []
let finished = 0
const arr = Array.from(promises)
if (arr.length === 0) return resolve([])
arr.forEach((p, i) => {
Promise.resolve(p).then(
value => {
results[i] = value
if (++finished === arr.length) resolve(results)
},
reject // 一个失败就整体失败
)
})
})
}
// 测试
Promise.myAll([Promise.resolve(1), Promise.resolve(2), 3]).then(console.log) // [1,2,3]
Promise.myAll([Promise.resolve(1), Promise.reject('err')]).catch(console.log) // 'err'3. Promise.race
推导链
Q1: race 干嘛?→ 谁先完成用谁(无论成功失败)
Q2: 怎么实现?→ 每个都 .then(resolve, reject),第一个触发后状态锁定
代码
Promise.myRace = function(promises) {
return new Promise((resolve, reject) => {
for (const p of promises) {
Promise.resolve(p).then(resolve, reject)
}
})
}
// 测试
Promise.myRace([
new Promise(r => setTimeout(() => r('slow'), 200)),
new Promise(r => setTimeout(() => r('fast'), 100))
]).then(console.log) // 'fast'4. Promise.allSettled
推导链
Q1: 和 all 区别?→ 不管成功失败,等全部完成
Q2: 返回格式?→
代码
Promise.myAllSettled = function(promises) {
return new Promise(resolve => {
const results = []
let finished = 0
const arr = Array.from(promises)
if (arr.length === 0) return resolve([])
arr.forEach((p, i) => {
Promise.resolve(p).then(
value => { results[i] = { status: 'fulfilled', value } },
reason => { results[i] = { status: 'rejected', reason } }
).finally(() => {
if (++finished === arr.length) resolve(results)
})
})
})
}
// 测试
Promise.myAllSettled([Promise.resolve(1), Promise.reject('err')])
.then(console.log) // [{status:'fulfilled',value:1}, {status:'rejected',reason:'err'}]5. Promise.any
推导链
Q1: 和 race 区别?→ 第一个成功的,全失败才失败
Q2: 全失败返回什么?→ AggregateError
代码
Promise.myAny = function(promises) {
return new Promise((resolve, reject) => {
const errors = []
let rejected = 0
const arr = Array.from(promises)
if (arr.length === 0) return reject(new AggregateError([], 'All promises were rejected'))
arr.forEach((p, i) => {
Promise.resolve(p).then(
resolve, // 第一个成功就 resolve
reason => {
errors[i] = reason
if (++rejected === arr.length) {
reject(new AggregateError(errors, 'All promises were rejected'))
}
}
)
})
})
}
// 测试
Promise.myAny([Promise.reject(1), Promise.resolve(2)]).then(console.log) // 2
Promise.myAny([Promise.reject(1), Promise.reject(2)]).catch(e => console.log(e.errors)) // [1,2]6. 并发控制
推导链
Q1: 什么场景?→ 10个请求,同时最多跑3个
Q2: 怎么控制?→ 维护 executing 池,满了就等
Q3: 怎么等?→ Promise.race 等最快完成的
Q4: 完成后?→ 从池子删掉,继续下一个
变体速查
| 形态 | 核心 |
|---|---|
| async 版 | await Promise.race(executing) |
| new Promise 版 | next() 递归驱动 |
代码
// 写法一:async + Promise.race
async function concurrent(tasks, limit) {
const res = [], executing = []
for (let i=0; i<tasks.length; i++) {
const p = tasks[i]().then(
r => {
res[i] = r
executing.splice(i)
}
)
executing.push(p)
if (executing.length >= limit) {
await Promise.race(executing)
}
}
Promise.all(executing)
return res
}
// 写法二:new Promise + next 递归
function concurrentV2(tasks, limit) {
return new Promise((resolve, reject) => {
let index = 0 ,finished = 0
const res = []
const len = tasks.length
function next() {
if (finished === len) resolve(res)
if (index === len) return
const i = index++
tasks[i]()
.then(v => {
res[i] = v
})
.catch(reject)
.finnaly( () => {
finished++
next()
})
}
for (let i=0; i < Math.min(limit, len); i++) next()
})
}
// 写法3, p-limit
function pLimit(concurrency) {
let activeCount = 0
const q = []
function next() {
if (q.length && activeCount < concurrency) {
const {fn, resolve, reject} = q.shift()
fn().then(resolve).catch(reject).finally(() => {activeCount--; next()})
}
}
return fn => new Promise((resolve, reject) => {q.push({fn, resolve, reject}); next()})
}
// 测试
// 测试任务:模拟异步请求,打印开始/结束时间
const createTasks = () => [1, 2, 3, 4, 5].map(n => () =>
new Promise(resolve => {
console.log(`任务${n}开始 - ${Date.now() % 10000}ms`)
setTimeout(() => {
console.log(`任务${n}完成 - ${Date.now() % 10000}ms`)
resolve(n * 10)
}, 100)
})
)
// 测试1:async + Promise.race
console.log('--- 测试 concurrent ---')
concurrent(createTasks(), 2).then(res => {
console.log('结果:', res) // [10, 20, 30, 40, 50]
})
// 测试2:new Promise + next 递归
setTimeout(() => {
console.log('\n--- 测试 concurrentV2 ---')
concurrentV2(createTasks(), 2).then(res => {
console.log('结果:', res) // [10, 20, 30, 40, 50]
})
}, 1000)
// 测试3:p-limit
setTimeout(() => {
console.log('\n--- 测试 pLimit ---')
const limit = pLimit(2)
const tasks = [1, 2, 3, 4, 5].map(n =>
limit(() => new Promise(resolve => {
console.log(`任务${n}开始 - ${Date.now() % 10000}ms`)
setTimeout(() => {
console.log(`任务${n}完成 - ${Date.now() % 10000}ms`)
resolve(n * 10)
}, 100)
}))
)
Promise.all(tasks).then(res => {
console.log('结果:', res) // [10, 20, 30, 40, 50]
})
}, 2000)7. retry重试 + 超时控制
推导链
Q1: retry 怎么实现?→ 失败就递归,次数减一
Q2: 超时怎么控制?→ Promise.race([请求, 超时Promise])
Q3: 带延迟?→ 失败后 setTimeout 再重试
代码
// 基础 retry
function retry(fn, times) {
return fn().catch(err => {
if (times <= 0) throw err
return retry(fn, times - 1)
})
}
// 带延迟的 retry
function retryWithBackoff(fn, maxTimes, baseDelay = 1000, attempt = 0) {
return fn().catch(err => {
if (attempt >= maxTimes) throw err
const delay = baseDelay * (2 ** attempt) // 1s, 2s, 4s, 8s...
return new Promise(r => setTimeout(r, delay))
.then(() => retryWithBackoff(fn, maxTimes, baseDelay, attempt + 1))
})
}
// 超时控制
function withTimeout(fn, timeout) {
return Promise.race([
fn(),
new Promise((_, reject) => setTimeout(() => reject(new Error('Timeout')), timeout))
])
}
// 组合:retry + timeout
function retryWithTimeout(fn, times, timeout) {
return retry(() => withTimeout(fn, timeout), times)
}
// 测试
let count = 0
const unstable = () => new Promise((res, rej) => ++count < 3 ? rej('fail') : res('ok'))
retry(unstable, 3).then(console.log) // 'ok'(第3次成功)8. sleep
推导链
Q1: sleep 干嘛?→ 等待指定时间
Q2: 怎么实现?→ Promise + setTimeout
代码
const sleep = (ms) => new Promise(r => setTimeout(r, ms))
// 同步阻塞版(不推荐,会卡主线程)
function sleepSync(ms) {
const start = Date.now()
while (Date.now() - start < ms) {}
}
// 测试
async function test() {
console.log('start')
await sleep(1000)
console.log('1s later')
}
test()9. 红绿灯循环
推导链
Q1: 需求?→ 红3s → 绿2s → 黄1s → 循环
Q2: 怎么串行?→ async/await 或 Promise 链
Q3: 怎么循环?→ 递归或 while(true)
代码
const sleep = (ms) => new Promise(r => setTimeout(r, ms))
async function trafficLight() {
while (true) {
console.log('🔴 红灯')
await sleep(3000)
console.log('🟢 绿灯')
await sleep(2000)
console.log('🟡 黄灯')
await sleep(1000)
}
}
// Promise 链版
function trafficLightV2() {
const light = (color, ms) => () => {
console.log(color)
return sleep(ms)
}
Promise.resolve()
.then(light('🔴 红灯', 3000))
.then(light('🟢 绿灯', 2000))
.then(light('🟡 黄灯', 1000))
.then(trafficLightV2) // 递归循环
}
trafficLight()10. Promisify
推导链
Q1: 干嘛用?→ 把 callback 风格转成 Promise 风格
Q2: callback 格式?→ fn(args..., (err, result) => {})
Q3: 怎么转?→ 返回新函数,内部 new Promise 包装
代码
function promisify(fn) {
return (...args) => new Promise((resolve, reject) => [
fn(...args, (e, r) => {
if (e !== null) reject(e)
else resolve(r)
})
])
}
// 模拟测试
const callbackFn = (a, b, cb) => setTimeout(() => cb(null, a + b), 100)
const asyncFn = promisify(callbackFn)
asyncFn(1, 2).then(console.log) // 311. 发布订阅 + 观察者模式
推导链
Q1: 发布订阅是什么?
→ 事件中心:on 订阅、emit 发布、off 取消
→ 发布者和订阅者不直接通信,通过事件中心
Q2: 观察者模式区别?
→ 观察者直接订阅目标,目标直接通知观察者
→ 没有中间的事件中心
Q3: 核心数据结构?
→ 发布订阅:{ eventName: [cb1, cb2] }
→ 观察者:observers: [observer1, observer2]
对比
| 发布订阅 | 观察者 | |
|---|---|---|
| 耦合度 | 松耦合(有中间层) | 紧耦合(直接引用) |
| 通信 | 通过事件中心 | 目标直接调观察者 |
| 场景 | EventEmitter、Vue事件总线 | MobX、Vue响应式 |
代码
// 发布订阅
class EventEmitter {
constructor() { this.events = {} }
on(event, cb) {
(this.events[event] ||= []).push(cb)
return this
}
emit(event, ...args) {
(this.events[event] || []).forEach(cb => cb(...args))
return this
}
off(event, cb) {
if (!cb) this.events[event] = []
else this.events[event] = (this.events[event] || []).filter(f => f !== cb && f.raw !== cb)
return this
}
once(event, cb) {
const wrapper = (...args) => { cb(...args); this.off(event, wrapper) }
wrapper.raw = cb
return this.on(event, wrapper)
}
}
// 观察者模式
class Subject {
constructor() { this.observers = [] }
add(observer) { this.observers.push(observer) }
remove(observer) { this.observers = this.observers.filter(o => o !== observer) }
notify(data) { this.observers.forEach(o => o.update(data)) }
}
class Observer {
constructor(name) { this.name = name }
update(data) { console.log(`${this.name} 收到: ${data}`) }
}
// 测试发布订阅
const bus = new EventEmitter()
bus.on('msg', console.log)
bus.emit('msg', 'hello') // 'hello'
// 测试观察者
const subject = new Subject()
const ob1 = new Observer('A')
const ob2 = new Observer('B')
subject.add(ob1)
subject.add(ob2)
subject.notify('更新了') // A 收到: 更新了, B 收到: 更新了12. CodingMan
推导链
Q1: 需求?→ 链式调用 + 异步任务队列
Q2: 怎么链式?→ 每个方法 return this
Q3: 怎么等链式调用完再执行?→ setTimeout 宏任务延迟启动
Q4: 怎么串行执行?→ 两种方案:next() 内部驱动 / for await 外部迭代
Q5: sleepFirst 怎么插队?→ unshift 到队列头部
变体速查
| 形态 | 核心 |
|---|---|
| next() 驱动 | 每个任务执行完调 this.next() |
| for await 迭代 | 任务返回 Promise,外部 await 遍历 |
代码
// 写法一:next() 内部驱动
function CodingMan(name) {
const queue = []
const next = () => {
const task = queue.shift()
task && task()
}
const ctx = {
eat(food) {
queue.push(() => {
console.log(`Eat ${food}`)
next()
})
return this
},
sleep(sec) {
queue.push(() => {
console.log(`Sleep ${sec}s...`)
setTimeout(() => { console.log('Wake up'); next() }, sec * 1000)
})
return this
},
sleepFirst(sec) {
queue.unshift(() => {
console.log(`Sleep ${sec}s...`)
setTimeout(() => { console.log('Wake up'); next() }, sec * 1000)
})
return this
}
}
// 初始任务
queue.push(() => { console.log(`Hi, I am ${name}`); next() })
// setTimeout 等链式调用完再启动
setTimeout(() => next())
return ctx
}
// 写法二:for await 外部迭代
function CodingManV2(name) {
const queue = []
const ctx = {
eat(food) {
queue.push(() => console.log(`Eat ${food}`))
return this
},
sleep(sec) {
queue.push(() => new Promise(r => {
console.log(`Sleep ${sec}s...`)
setTimeout(() => { console.log('Wake up'); r() }, sec * 1000)
}))
return this
},
sleepFirst(sec) {
queue.unshift(() => new Promise(r => {
console.log(`Sleep ${sec}s...`)
setTimeout(() => { console.log('Wake up'); r() }, sec * 1000)
}))
return this
}
}
// 初始任务
queue.push(() => console.log(`Hi, I am ${name}`))
// setTimeout 等链式调用完,然后 for await 遍历执行
setTimeout(async () => {
for (const task of queue) await task()
})
return ctx
}
// 测试
CodingMan('Jack').eat('breakfast').sleep(2).eat('lunch')
// Hi, I am Jack → Eat breakfast → Sleep 2s... → Wake up → Eat lunch
CodingMan('Jack').sleepFirst(2).eat('breakfast')
// Sleep 2s... → Wake up → Hi, I am Jack → Eat breakfast