定时器和promise是协同而非对立的异步基础组件,前者控制执行时机(timers阶段),后者管理状态流转(微任务队列),高性能库需在两者基础上叠加并发、重试、节流等能力。

定时器和Promise本身不是对立关系,而是可以深度协同的异步控制基础组件。真正高性能的异步流控制库,不是简单封装 setTimeout 或 Promise.resolve,而是让两者在事件循环各阶段精准配合——比如用定时器调度执行时机,用Promise管理状态流转与错误边界,再叠加并发、重试、竞态等业务逻辑。
定时器不只是延时:它是事件循环的节奏控制器
setTimeout 和 setInterval 属于 Node.js 事件循环的 timers 阶段,而 Promise.then 回调属于微任务队列(microtask queue),会在当前宏任务结束后立即执行。这个执行顺序差异是构建可控流的关键。
- 用 setTimeout 控制“何时开始”:比如防抖、延迟初始化、失败后退避重试(backoff)
- 用 Promise 封装“是否成功”:把定时器回调包装成 Promise,统一 resolve/reject 接口,便于链式组合
- 避免混用陷阱:不要在 Promise 构造函数里无条件写 setTimeout(() => resolve(), 0),这会把微任务变成宏任务,破坏时序预期;应优先使用 Promise.resolve().then() 实现“下一帧”调度
Promise 是状态容器,不是执行引擎
原生 Promise 不提供并发限制、取消、缓存或重试能力。一个高性能库必须在其之上补充这些缺失层,而定时器常作为底层支撑:
- 超时控制:Promise.race([apiCall(), new Promise((_, reject) => setTimeout(() => reject(new Error('timeout')), 5000))])
- 节流/限频:维护一个 lastRun 时间戳 + setTimeout 延迟下一次执行,结合 Promise 包装返回值,保证调用方始终拿到 Promise 实例
- 自动重试:失败后根据指数退避策略(如 100ms → 200ms → 400ms)用 setTimeout 触发下一次 Promise 执行
组合模式比单点封装更有扩展性
不建议只做一个“带 timeout 的 fetch”,而应设计可组合的原子能力:
- createTimeout(promise, ms) → 返回带超时语义的新 Promise
- createRetry(fn, { max: 3, backoff: 'exponential' }) → 返回一个函数,每次调用都返回 Promise,并内置定时重试逻辑
- createThrottle(fn, delay) → 返回函数,确保 fn 最多每 delay 毫秒执行一次,且每次调用都返回对应 Promise
- 所有函数都保持参数透传、类型保留、错误冒泡一致,方便嵌套使用:createTimeout(createRetry(fetchUser), 8000)
避开常见性能雷区
很多“高性能”库实际因细节失衡反而拖慢应用:
- 避免在大量 Promise 中频繁创建新定时器(如每个请求都 new Timeout)→ 改用共享 timer queue 或 requestIdleCallback 协调
- 不要用 setInterval 模拟轮询 → 改用递归 Promise 链 + 动态 delay,更易中断和调试
- 微任务过多会阻塞渲染(浏览器)或 I/O(Node)→ 对批量操作使用 Promise.allSettled 而非全链式 then,减少微任务堆积
- 清除定时器要彻底:setTimeout 返回的 id 必须配 clearTimeout;若 Promise 已 resolve/reject,对应定时器即使未触发也应 clearTimeout 防内存泄漏











