Асинхронное отслеживание контекста¶
Стабильность: 2 – Стабильная
АПИ является удовлетворительным. Совместимость с NPM имеет высший приоритет и не будет нарушена кроме случаев явной необходимости.
Введение¶
Эти классы используются для связывания состояния и распространения его через обратные вызовы и цепочки промисов. Они позволяют хранить данные в течение всего времени жизни веб-запроса или любого другого асинхронного действия. Это похоже на локальное хранение потоков в других языках.
Классы AsyncLocalStorage
и AsyncResource
являются частью модуля node:async_hooks
:
1 2 3 4 |
|
1 2 3 4 |
|
Класс: AsyncLocalStorage
¶
Этот класс создает хранилища, которые остаются целостными благодаря асинхронным операциям.
Хотя вы можете создать свою собственную реализацию поверх модуля node:async_hooks
, AsyncLocalStorage
следует предпочесть, поскольку это производительная и безопасная для памяти реализация, включающая значительные оптимизации, которые неочевидны для реализации.
Следующий пример использует AsyncLocalStorage
для создания простого регистратора, который присваивает идентификаторы входящим HTTP-запросам и включает их в сообщения, регистрируемые в каждом запросе.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
|
Каждый экземпляр AsyncLocalStorage
поддерживает независимый контекст хранения. Несколько экземпляров могут безопасно существовать одновременно без риска вмешательства в данные друг друга.
new AsyncLocalStorage()
¶
Создает новый экземпляр AsyncLocalStorage
. Хранилище предоставляется только в рамках вызова run()
или после вызова enterWith()
.
Статический метод: AsyncLocalStorage.bind(fn)
¶
Стабильность: 1 – Экспериментальная
Экспериментальный
fn
<Function>
Функция для привязки к текущему контексту выполнения.- Возвращает:
<Function>
Новая функция, которая вызываетfn
в захваченном контексте выполнения.
Привязывает заданную функцию к текущему контексту выполнения.
Статический метод: AsyncLocalStorage.snapshot()
¶
Стабильность: 1 – Экспериментальная
Экспериментальная
- Возвращает:
<Function>
Новая функция с сигнатурой(fn: (...args) : R, ...args) : R
.
Захватывает текущий контекст выполнения и возвращает функцию, принимающую функцию в качестве аргумента. Всякий раз, когда вызывается возвращаемая функция, она вызывает переданную ей функцию в захваченном контексте.
1 2 3 4 |
|
AsyncLocalStorage.snapshot()
может заменить использование AsyncResource
для простых целей отслеживания асинхронного контекста, например:
1 2 3 4 5 6 7 8 9 10 11 12 |
|
asyncLocalStorage.disable()
¶
Стабильность: 1 – Экспериментальная
Экспериментальный
Отключает экземпляр AsyncLocalStorage
. Все последующие вызовы asyncLocalStorage.getStore()
будут возвращать undefined
, пока asyncLocalStorage.run()
или asyncLocalStorage.enterWith()
не будут вызваны снова.
При вызове asyncLocalStorage.disable()
, все текущие контексты, связанные с экземпляром, будут выведены.
Вызов asyncLocalStorage.disable()
необходим перед тем, как asyncLocalStorage
может быть собран в мусор. Это не относится к хранилищам, предоставляемым asyncLocalStorage
, так как эти объекты собираются вместе с соответствующими асинхронными ресурсами.
Используйте этот метод, когда asyncLocalStorage
больше не используется в текущем процессе.
asyncLocalStorage.getStore()
¶
- Возвращает:
<any>
Возвращает текущее хранилище. Если вызывается вне асинхронного контекста, инициализированного вызовом asyncLocalStorage.run()
или asyncLocalStorage.enterWith()
, возвращает undefined
.
asyncLocalStorage.enterWith(store)
¶
Стабильность: 1 – Экспериментальная
Экспериментальный
store
<any>
Переходит в контекст на оставшуюся часть текущего синхронного выполнения, а затем сохраняет хранилище в любых последующих асинхронных вызовах.
Пример:
1 2 3 4 5 6 7 |
|
Этот переход будет продолжаться в течение всего синхронного выполнения. Это означает, что если, например, контекст введен в обработчике событий, то последующие обработчики событий также будут выполняться в этом контексте, если только они специально не привязаны к другому контексту с помощью AsyncResource
. Вот почему run()
следует предпочесть enterWith()
, если только нет веских причин использовать последний метод.
1 2 3 4 5 6 7 8 9 10 11 12 |
|
asyncLocalStorage.run(store, callback[, ...args])
¶
store
<any>
callback
<Function>
...args
<any>
Выполняет функцию синхронно в контексте и возвращает ее возвращаемое значение. Хранилище недоступно за пределами функции обратного вызова. Хранилище доступно для любых асинхронных операций, созданных внутри обратного вызова.
Необязательные args
передаются в функцию обратного вызова.
Если функция обратного вызова выдает ошибку, то она также выдает и run()
. Этот вызов не влияет на трассировку стека, и контекст выходит из него.
Пример:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
|
asyncLocalStorage.exit(callback[, ...args])
¶
Стабильность: 1 – Экспериментальная
Экспериментальная
callback
<Function>
...args
<any>
Запускает функцию синхронно вне контекста и возвращает ее возвращаемое значение. Хранилище недоступно в функции обратного вызова или асинхронных операциях, созданных в рамках обратного вызова. Любой вызов getStore()
, выполненный внутри функции обратного вызова, всегда будет возвращать undefined
.
Необязательные args
передаются в функцию обратного вызова.
Если функция обратного вызова выдает ошибку, то она также выдается функцией exit()
. Этот вызов не влияет на трассировку стека, а контекст вводится заново.
Пример:
1 2 3 4 5 6 7 8 9 10 11 |
|
Использование с async/await
¶
Если в рамках асинхронной функции в контексте должен выполняться только один вызов await
, следует использовать следующий шаблон:
1 2 3 4 5 6 |
|
В этом примере хранилище доступно только в функции обратного вызова и в функциях, вызываемых foo
. Вне run
вызов getStore
вернет undefined
.
Устранение неполадок: Потеря контекста¶
В большинстве случаев AsyncLocalStorage
работает без проблем. В редких ситуациях текущее хранилище теряется в одной из асинхронных операций.
Если ваш код основан на обратных вызовах, достаточно промисифицировать его с помощью util.promisify()
, чтобы он начал работать с родными промисами.
Если вам необходимо использовать API на основе обратного вызова или ваш код предполагает пользовательскую реализацию thenable, используйте класс AsyncResource
, чтобы связать асинхронную операцию с правильным контекстом выполнения. Найдите вызов функции, ответственный за потерю контекста, записывая в журнал содержимое asyncLocalStorage.getStore()
после вызовов, которые, как вы подозреваете, ответственны за потерю. Если код регистрирует undefined
, то, вероятно, за потерю контекста отвечает последний вызванный обратный вызов.
Класс: AsyncResource
¶
Класс AsyncResource
предназначен для расширения асинхронных ресурсов embedder'а. Используя его, пользователи могут легко запускать события времени жизни своих собственных ресурсов.
Хук init
срабатывает при инстанцировании AsyncResource
.
Ниже приведен обзор API AsyncResource
.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
|
новый AsyncResource(type[, options])
¶
type
<string>
Тип асинхронного события.options
<Object>
triggerAsyncId
<number>
ID контекста выполнения, который создал это асинхронное событие. По умолчанию:executionAsyncId()
.requireManualDestroy
<boolean>
Если установлено значениеtrue
, отключаетemitDestroy
, когда объект собирается в мусор. Обычно это значение не нужно устанавливать (даже еслиemitDestroy
вызывается вручную), если только не полученasyncId
ресурса и с ним не вызываетсяemitDestroy
чувствительного API. Если установлено значениеfalse
, вызовemitDestroy
на сборку мусора будет происходить только при наличии хотя бы одного активного хукаdestroy
. По умолчанию:false
.
Пример использования:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
|
Статический метод: AsyncResource.bind(fn[, type[, thisArg]])
¶
fn
<Function>
Функция для привязки к текущему контексту выполнения.type
<string>
Необязательное имя, которое нужно связать с базовымAsyncResource
.thisArg
<any>
Привязывает данную функцию к текущему контексту выполнения.
asyncResource.bind(fn[, thisArg])
¶
fn
<Function>
Функция для привязки к текущемуAsyncResource
.thisArg
<any>
Привязывает данную функцию для выполнения к области видимости этого AsyncResource
.
asyncResource.runInAsyncScope(fn[, thisArg, ...args])
¶
fn
<Function>
Функция для вызова в контексте выполнения этого асинхронного ресурса.thisArg
<any>
Приемник, который будет использоваться для вызова функции....args
<any>
Необязательные аргументы для передачи функции.
Вызов предоставленной функции с предоставленными аргументами в контексте выполнения асинхронного ресурса. Это установит контекст, запустит AsyncHooks до обратных вызовов, вызовет функцию, запустит AsyncHooks после обратных вызовов, а затем восстановит исходный контекст выполнения.
asyncResource.emitDestroy()
¶
- Возвращает:
<AsyncResource>
Ссылка наasyncResource
.
Вызывает все хуки destroy
. Это должно быть вызвано только один раз. Если он будет вызван более одного раза, будет выдана ошибка. Это должно быть вызвано вручную. Если ресурс оставлен для сбора GC, то хуки destroy
никогда не будут вызваны.
asyncResource.asyncId()
¶
- Возвращает:
<number>
УникальныйasyncId
, присвоенный ресурсу.
asyncResource.triggerAsyncId()
¶
- Возвращает:
<number>
Тот жеtriggerAsyncId
, который передается в конструкторAsyncResource
.
Использование AsyncResource
для пула потоков Worker
¶
Следующий пример показывает, как использовать класс AsyncResource
для правильного обеспечения асинхронного отслеживания для пула Worker
. Другие пулы ресурсов, такие как пулы соединений с базами данных, могут следовать аналогичной модели.
Предположим, что задачей является сложение двух чисел, используя файл с именем task_processor.js
со следующим содержимым:
1 2 3 4 |
|
1 2 3 4 |
|
Пул рабочих вокруг него может использовать следующую структуру:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 |
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 |
|
Без явного отслеживания, добавляемого объектами WorkerPoolTaskInfo
, может показаться, что обратные вызовы связаны с отдельными объектами Worker
. Однако, создание Worker
не связано с созданием задач и не предоставляет информацию о том, когда задачи были запланированы.
Этот пул можно использовать следующим образом:
1 2 3 4 5 6 7 8 9 10 11 12 |
|
1 2 3 4 5 6 7 8 9 10 11 12 |
|
Интеграция AsyncResource
с EventEmitter
¶
Слушатели событий, запускаемые EventEmitter
, могут быть запущены в другом контексте выполнения, чем тот, который был активен при вызове eventEmitter.on()
.
В следующем примере показано, как использовать класс AsyncResource
для правильного связывания слушателя событий с правильным контекстом выполнения. Тот же подход можно применить к Stream
или аналогичному классу, управляемому событиями.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
|