Что и почему было deprecated в node:async_hooks?nodejs-48

1. Введение в проблематику

Модуль async_hooks в Node.js претерпел значительные изменения с момента своего появления. Некоторые функции были помечены как deprecated (устаревшие) по следующим причинам:

  • Проблемы с производительностью
  • Сложность корректной реализации
  • Появление более эффективных альтернатив
  • Поддержка стабильности API

2. Основные устаревшие функции

2.1. asyncResource.emitBefore и asyncResource.emitAfter

Почему устарели:

  • Могли вызывать несоответствия в контексте выполнения
  • Создавали проблемы при вложенных вызовах
  • Альтернативы обеспечивают лучшую производительность

Пример устаревшего кода:

const async_hooks = require('node:async_hooks');
const hook = async_hooks.createHook({
  init(asyncId, type, triggerAsyncId) {
    // Устаревший подход
    asyncResource.emitBefore();
    // Логика...
    asyncResource.emitAfter();
  }
});

2.2. async_hooks.executionAsyncId

Причины устаревания:

  • Высокие накладные расходы
  • Неоднозначное поведение в некоторых сценариях
  • Заменен на более предсказуемые механизмы

Устаревшее использование:

// Устаревший способ
const currentId = async_hooks.executionAsyncId();

3. Альтернативы устаревшим функциям

3.1. Использование AsyncLocalStorage

Рекомендуемая замена:

const { AsyncLocalStorage } = require('node:async_hooks');
const asyncLocalStorage = new AsyncLocalStorage();

function logWithId(msg) {
  const id = asyncLocalStorage.getStore();
  console.log(`${id !== undefined ? id : '-'}:`, msg);
}

asyncLocalStorage.run(123, () => {
  logWithId('Hello');  // 123: Hello
});

3.2. async_hooks.createHook с ограниченными событиями

Современный подход:

const async_hooks = require('node:async_hooks');

const hook = async_hooks.createHook({
  init(asyncId, type, triggerAsyncId, resource) {
    // Только необходимые события
  }
});

4. Причины устаревания конкретных API

4.1. Проблемы производительности

Некоторые API async_hooks могли замедлять приложение на 20-30%

4.2. Сложность поддержки

Реализация корректной работы всех хуков требовала значительных усилий

4.3. Непредсказуемое поведение

Некоторые сценарии использования приводили к утечкам памяти или неожиданным ошибкам

5. Влияние на существующий код

5.1. Как обновить устаревший код

  1. Заменить emitBefore/emitAfter на AsyncLocalStorage
  2. Использовать asyncLocalStorage.getStore() вместо executionAsyncId()
  3. Ограничить использование хуков только необходимыми событиями

5.2. Пример миграции

До:

const asyncResource = new async_hooks.AsyncResource('my-type');
asyncResource.emitBefore();
try {
  // Логика
} finally {
  asyncResource.emitAfter();
}

После:

const { AsyncLocalStorage } = require('node:async_hooks');
const asyncLocalStorage = new AsyncLocalStorage();

asyncLocalStorage.run(new AsyncResource('my-type'), () => {
  // Логика
});

6. Текущие рекомендации по использованию async_hooks

  1. Используйте AsyncLocalStorage для большинства сценариев
  2. Избегайте активных хуков в production если возможно
  3. Ограничьте отслеживаемые события только необходимыми
  4. Тестируйте производительность при использовании API

Резюмируем:

Node.js постепенно отказывается от проблемных API в async_hooks в пользу более стабильных и производительных альтернатив. Основные изменения коснулись механизмов отслеживания контекста выполнения, которые были заменены на AsyncLocalStorage. При работе с асинхронными контекстами следует использовать современные API, которые обеспечивают лучшую производительность и предсказуемость поведения.