用 Map 集中跟踪 Promise 状态的核心是将唯一标识作为键、含 promise/startedAt/abortController/status 的元数据对象作为值存储,通过任务完成、超时异常、定期扫描三种方式主动清理,结合 p-map 实现批量可控跟踪,并以 size 提供可观测性。

用 Map 集中跟踪 Promise 状态,核心是把每个异步任务的唯一标识(如 request ID、用户 session key 或时间戳)作为键,把任务相关元数据(Promise 实例、开始时间、状态标记、结果缓存或 abortController)作为值存入。它不直接“监控 Promise”,而是通过你主动管理的生命周期来实现可查、可控、可清理的状态中心。
用 Map 存什么:结构决定可维护性
别只存 Promise 对象本身——那无法判断是否完成、超时或被取消。推荐结构:
-
id:字符串键,建议带业务前缀(如
"api:order-123"或"ws:u789:ping") - promise:原始 Promise 实例(用于 await 或 .then 链接)
-
startedAt:
Date.now(),用于超时判断和 LRU 清理 - abortController(可选):便于主动中止未完成请求
-
status(可选):
'pending' | 'fulfilled' | 'rejected' | 'aborted',显式记录状态,避免反复检查 Promise 内部(Promise 状态不可读)
如何触发清理:靠主动检查,不是自动监听
Map.prototype.size 是只读属性,不能监听变化。清理必须由你主动触发,常见时机有三类:
-
任务完成时清理:在 Promise 的
.then()和.catch()末尾调用map.delete(id);若用了async/await,在try/catch/finally的finally块里删 -
超时或异常时清理:启动 Promise 同时设定时器,超时后
abortController?.abort()并map.delete(id);也可封装成统一的withTimeout(promise, ms)工具函数 -
定期兜底清理:用
setInterval每 15–30 秒扫描一次 Map,移除startedAt超过阈值(如 5 分钟)且仍为'pending'的项
结合 p-map 实现批量任务的受控跟踪
当你用 p-map 并发执行一批异步操作时,可在映射函数中同步注册到 Map:
const taskMap = new Map();
const results = await pMap(items, async (item) => {
const id = `batch:${Date.now()}:${Math.random().toString(36).slice(2, 8)}`;
const controller = new AbortController();
taskMap.set(id, {
promise: null,
startedAt: Date.now(),
abortController: controller,
status: 'pending'
});
try {
const result = await fetchItem(item, { signal: controller.signal });
taskMap.get(id).status = 'fulfilled';
return result;
} catch (err) {
taskMap.get(id).status = 'rejected';
throw err;
} finally {
// 不立即删除,留待后续按需清理或统计
}
}, { concurrency: 4 });
这样既享受了 p-map 的并发控制,又保有了每个任务的上下文,便于调试、中断或导出当前运行中的任务列表。
暴露可观测性:让 size 成为真实指标
taskMap.size 是最轻量的实时负载信号。你可以:
- 前端开发时,在控制台直接输入
taskMap.size查看当前挂起任务数 - Node.js 服务中,提供
GET /metrics接口返回{"pendingTasks": taskMap.size},接入 Prometheus - 设置告警:当
taskMap.size > 200持续 1 分钟,触发 Slack 通知
注意:不要用 size 做唯一清理依据——要结合时间戳或显式状态,否则可能误删刚进入 pending 的合法任务。

















