В production — от SPA до Node.js сервисов — асинхронные пайплайны множатся, но отмену часто сводят к единичному сигналу для HTTP. Ошибка: считают, что AbortController решает всё автоматом, забывая про ручную очистку таймеров, стримов и сокетов. Ресурсы текут тихо, пока не наступит продакшн-баг с утечкой памяти.
Проверяй aborted после каждого await
Если пайплайн из нескольких шагов — не рассчитывай, что один сигнал магически остановит все. После каждого
await проверяй signal.aborted. Иначе async-функции продолжат выполняться, пока не дойдут до точки, где AbortError всё же возникнет, но уже поздно.Связывай сигналы для retry и композиций
При повторных попытках не плоди новые контроллеры в отрыве от внешнего. Привяжись через addEventListener с флагом once — так отмена пробрасывается корректно, а cleanup в catch предотвращает мёртвые таймеры:
async function retryWithAbort(url, signal) {
const inner = new AbortController();
signal.addEventListener('abort', () => inner.abort(), { once: true });
try {
return await fetch(url, { signal: inner.signal });
} catch (err) {
if (err.name === 'AbortError') cleanup();
throw err;
}
}AbortSignal.any() — когда отмена приходит с разных сторон
Тайм-аут, действие пользователя, сигнал из микрофронтенда — объедини сигналы через
AbortSignal.any(). Это снижает дублирование логики и упрощает очистку: один слушатель, один контроллер.Вывод: Без явной проверки
signal.aborted и явного cleanup в каждом звене асинхронный пайплайн гарантированно утекает, а отладка таких багов требует часов, а не минут.