Как создать общий контекст между асинхронными вызовами в nodejs

👋 Всем привет!!!!

Как разработчик javascript, даже если вы не очень часто реализуете свои собственные асинхронные функции, вы, скорее всего, будете вынуждены использовать их в своем повседневном проекте.

Обычно существует два/три способа работы с асинхронными функциями.

  1. Обратные вызовы
  2. Обещания
  3. Async/Await (т.е. Promises).

Подробнее об этом можно прочитать здесь.

Постановка проблемы

Когда у вас есть цепочка асинхронных вызовов (обратные вызовы или обещания), как разделить общий контекст между всеми этими вызовами?

Рассмотрим следующий пример.

Вы пишете функцию getCustomerOrders(), которая возвращает данные клиента вместе с его/ее активными заказами. Внутри этой функции вы должны вызвать асинхронную getCustomer() и асинхронную getOrders(), где обеим функциям нужен customerId из запроса.

Решение простое, верно? 😊

Вы просто извлекаете customerId из запроса и передаете его в getCustomer() и getOrders() в качестве параметров функции.

const getCustomer = async (customerId: string): Promise<Customer> => {
    return fetchCustomerFromApi(customerId);
};
Вход в полноэкранный режим Выйти из полноэкранного режима

Да, это, вероятно, лучший способ обмена контекстом между асинхронными вызовами. Но знаете ли вы альтернативный способ обмена контекстом без передачи в качестве параметров?

AsyncLocalStorage

Класс AsyncLocalStorage модуля async_hooks выпущен как часть Node.js 14.

Согласно официальной документации NodeJS

Эти классы используются для связывания состояния и распространения его через обратные вызовы и цепочки обещаний. Они позволяют хранить данные в течение всего времени выполнения веб-запроса или любого другого асинхронного действия. Это похоже на потоково-локальное хранение в других языках.

Проще говоря, это глобальная переменная, привязанная к определенному контексту асинхронного выполнения.

Давайте посмотрим AsyncLocalStorage в действии

Давайте посмотрим, как мы можем рефакторить наш пример getCustomerOrders() для использования AsyncLocalStorage.

  1. Во-первых, импортируйте AsyncLocalStorage из модуля async_hooks.
import { AsyncLocalStorage } from "async_hooks";
Вход в полноэкранный режим Выход из полноэкранного режима
  1. Далее необходимо создать экземпляр из AsyncLocalStorage, представляющий данные, которыми вы собираетесь обмениваться. В этом примере мы будем хранить customerId.
const userAsyncLocalStorage = new AsyncLocalStorage<{ customerId: string }>();
Вход в полноэкранный режим Выход из полноэкранного режима
  1. Теперь необходимо обернуть функцию getCustomerOrders() с помощью функции AsyncLocalStorage.run. Именно здесь и происходит вся магия. В качестве первого параметра функции run вы можете передать данные, которыми хотите поделиться.
userAsyncLocalStorage.run(
    {
      // customerId is read from the Request
      customerId: "123456789",
    },
    async () => {
      const customer = await getCustomer();
      const orders = await getOrders();
      // write data to Response 
      console.log({
        customer,
        orders,
      });
    }
  );
Вход в полноэкранный режим Выход из полноэкранного режима
  1. Наконец, внутри функций getCustomer() и getOrders() вы можете получить customerId, как показано ниже.
const getCustomer = async () => {
    const { customerId } = userAsyncLocalStorage.getStore();
    return fetchCustomerFromApi(customerId);
}
Вход в полноэкранный режим Выход из полноэкранного режима

Это конец очень простого приложения, использующего AsyncLocalStorage.

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

Глобальное состояние или переменные обычно считаются плохими
поскольку они значительно усложняют тестирование и отладку. Поэтому не рекомендуется использовать AsyncLocalStorage для обмена бизнес-данными между несколькими асинхронными вызовами (как мы делимся customerId).

Но паттерн AsyncLocalStorage может пригодиться, когда вы разрабатываете/используете инструменты APM, которые собирают показатели производительности.

В этом посте объясняется, как можно использовать AsyncLocalStorage для создания простого компонента логгера.

Также NodeJS Frameworks, например adonisjs, широко использует AsyncLocalStorage во время HTTP запросов и устанавливает HTTP контекст в качестве состояния.

Подробнее об этом вы можете прочитать здесь.

❤️ Ценю ваш отзыв и большое спасибо за прочтение…!!!

Оцените статью
Procodings.ru
Добавить комментарий