用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
直接命令不会经过审查 Prompt;运行前请先检查来源。
npx skills add https://github.com/SocketDev/action --skill plugging-promise-race命令会保持在同一行。复制前请横向滚动并检查完整内容。
想先保存到本地?可下载 SkillsMP 当前能够提供的文件。
Propagate a wheelhouse template change across fleet repos: worktrees, push/PR fallback, cleanup.
Run this repo's GitHub Actions locally with Agent-CI before pushing CI-sensitive changes.
Audit package exports for dead, internal-only, or weakly-consumed subpaths before pruning.
基于 SOC 职业分类
正在显示 SKILL.md
| name | plugging-promise-race |
| description | Reference for avoiding Promise.race/any handler leaks in loops and hand-rolled concurrency pools. |
| user-invocable | false |
| allowed-tools | Read, Grep, Glob |
| metadata | {"internal":true} |
Never re-race the same pool of promises across loop iterations. Each call to Promise.race([A, B, …]) attaches fresh .then handlers to every arm. A promise that survives N iterations accumulates N handler sets. See nodejs/node#17469 and @watchable/unpromise.
Safe — both arms created per call:
const value = await Promise.race([
fetchSomething(),
new Promise((_, r) => setTimeout(() => r(new Error('timeout')), 5000)),
])
Leaky — pool survives across iterations, accumulating handlers:
while (queue.length) {
const winner = await Promise.race(pool) // ← N handlers per arm by iteration N
pool = pool.filter(p => p !== winner)
}
Same hazard for Promise.any and any long-lived arm such as an interrupt signal.
Use a single-waiter "slot available" signal. Each task's .then resolves a one-shot promiseWithResolvers that the loop awaits, then replaces. No persistent pool, nothing to stack.
let signal = Promise.withResolvers<Task>()
function startTask(task: Task) {
task.run().then(() => {
const prev = signal
signal = Promise.withResolvers<Task>()
prev.resolve(task)
})
}
while (queue.length) {
// launch up to N tasks
while (running < N && queue.length) startTask(queue.shift()!)
const finished = await signal.promise
running -= 1
}
The arm being awaited is always fresh; nothing accumulates handlers.
Before merging concurrency code, ask: does any arm of a Promise.race/Promise.any outlive the call? If yes, refactor to the single-waiter signal.