Вызов акторов
Конечные автоматы могут «вызывать» одного или нескольких акторов в заданном состоянии. Вызванный актор запускается при входе в состояние и останавливается при выходе из него. Можно вызвать любой актор XState, включая простых Promise-акторов или даже сложных акторов на основе автоматов.
Вызов актора полезен для управления синхронной или асинхронной работой, которую конечный автомат должен оркестрировать и с которой взаимодействовать на высоком уровне, но не нуждается в детальном знании о ней.
Акторы могут быть вызваны в любом состоянии, кроме финального состояния верхнего уровня. В следующем примере состояние loading вызывает Promise-актор:
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 | |
Акторы также могут быть вызваны в корне автомата, и они будут активны в течение всего времени жизни родительского актора автомата:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | |
И invoke может быть массивом для вызова нескольких акторов:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | |
Дополнительные примеры:
- Рабочий процесс с повторным использованием функций и определений событий
- Периодическая проверка почтового ящика (cron-рабочий процесс)
- Проверка показателей автомобиля (SubFlow Repeat) рабочий процесс
Чем акторы отличаются от действий?¶
Действия выполняются по принципу «запустил и забыл»; как только их выполнение начинается, конечный автомат, выполняющий действия, забывает о них. Если вы укажете действие как async, действие не будет ожидаться перед переходом в следующее состояние. Помните: переходы всегда мгновенны (состояния переходят синхронно).
Вызванные акторы могут выполнять асинхронную работу и взаимодействовать со своим родительским актором автомата. Они могут отправлять и получать события. Вызванные акторы автоматов могут даже вызывать или порождать собственных дочерних акторов.
В отличие от действий, ошибки, выбрасываемые вызванными акторами, могут быть обработаны напрямую:
1 2 3 4 5 6 7 8 9 | |
В то время как ошибки, выбрасываемые действиями, могут быть обработаны только глобально подписчиком их родительского конечного автомата:
1 2 3 4 5 | |
Жизненный цикл¶
Вызванные акторы имеют жизненный цикл, управляемый состоянием, в котором они вызваны. Они создаются и запускаются при входе в состояние и останавливаются при выходе из него.
Если в состояние входят и сразу же выходят из него, например, из-за безусловного («always») перехода, то в этом состоянии акторы не будут вызваны.
Повторный вход¶
По умолчанию, когда конечный автомат переходит из родительского состояния в то же родительское состояние или в потомка (дочернее или глубже), он не повторно входит в родительское состояние. Поскольку переход не является повторным входом, существующие вызванные акторы родительского состояния не будут остановлены, и новые вызванные акторы не будут запущены.
Однако, если вы хотите, чтобы переход повторно вошёл в родительское состояние, установите свойство reenter перехода в true. Переходы, которые повторно входят в состояние, будут останавливать существующих вызванных акторов и запускать новых.
Подробнее о повторном входе в состояния.
API свойства invoke¶
Вызов определяется в конфигурации узла состояния с помощью свойства invoke, значение которого является объектом, содержащим:
src- Источник логики актора для вызова при создании актора, или строка, ссылающаяся на логику актора, определённую в предоставленной реализации автомата.id- Строка, идентифицирующая актор, уникальная в пределах родительского автомата.input- Входные данные для передачи актору.onDone- Переход, который происходит при завершении актора.onError- Переход, который происходит при выбросе ошибки актором.onSnapshot- Переход, который происходит при генерации актором нового значения.systemId- Строка, идентифицирующая актор, уникальная в масштабе всей системы.
Источник¶
src представляет логику актора, которую автомат должен использовать при создании актора. В XState доступно несколько создателей логики актора:
- Логика конечного автомата (
createMachine) - Логика промиса (
fromPromise), где invoke выполнит переходonDoneприresolveили переходonErrorприreject - Логика функции перехода (
fromTransition), которая следует паттерну редьюсера - Логика Observable (
fromObservable), которая может отправлять события родительскому автомату, и где invoke выполнит переходonDoneпри завершении - Логика Event Observable (
fromEventObservable), как логика Observable, но для потоков объектов событий - Логика колбэка (
fromCallback), которая может отправлять события родительскому автомату и получать от него
src вызова может быть встроенным или предоставленным.
Встроенный src¶
Либо напрямую встроенный:
1 2 3 | |
Либо из некоторой логики в той же области видимости, что и автомат:
1 2 3 4 5 6 7 | |
Предоставленный src¶
src может быть предоставлен в реализации автомата и указан с помощью строки или объекта.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | |
onDone¶
- Переход при завершении вызванного актора
- Свойство
outputобъекта события предоставляется с выходными данными актора - Недоступно для callback-акторов
Внимание
Не путайте свойство onDone состояния с invoke.onDone — это похожие переходы, но они относятся к разным вещам.
- Свойство
onDoneна узле состояния относится к достижению составным узлом состояния финального состояния. - Свойство
invoke.onDoneотносится к завершению вызова (invoke.src).
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | |
Переход onDone может быть объектом:
1 2 3 4 5 6 7 8 9 10 11 12 13 | |
Или, для простоты, переходы только с целью могут быть строками:
1 2 3 4 5 6 7 8 | |
onError¶
- Переход при выбросе ошибки вызванным актором, или (для Promise-акторов) при отклонении промиса
- Свойство
errorобъекта события предоставляется с данными ошибки актора
Переход onError может быть объектом:
1 2 3 4 5 6 7 8 9 10 11 | |
Или, для простоты, переходы только с целью могут быть строками:
1 2 3 4 5 6 7 8 | |
onSnapshot¶
- Переход при генерации вызванным актором нового снимка
- Событие получает
snapshotсо снимком актора - Недоступно для callback-акторов
1 2 3 4 5 6 7 8 | |
Входные данные¶
Для определения входных данных вызванного актора используйте input.
Свойство input может быть статическим входным значением или функцией, возвращающей входное значение. Функция получит объект, содержащий текущий context и event.
Совет
За кулисами input передаётся актору через событие: { type: 'xstate.init', input: ... }.
Входные данные из статического значения¶
1 2 3 4 5 6 7 8 | |
Входные данные из функции¶
1 2 3 4 5 6 7 8 9 10 11 | |
См. Входные данные для подробностей.
Вызов промисов¶
Наиболее распространённый тип акторов, которые вы будете вызывать — это promise-акторы. Promise-акторы позволяют дождаться результата промиса, прежде чем решать, что делать дальше.
XState может вызывать промисы как акторов с помощью создателя логики актора fromPromise. Промисы могут:
resolve(), что выполнит переходonDonereject()(или выбросить ошибку), что выполнит переходonError
Если состояние, в котором активен вызванный промис, покидается до завершения промиса, результат промиса отбрасывается.
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 | |
Разрешённый вывод помещается в событие 'xstate.done.actor.<id>' в свойстве output, например:
1 2 3 4 5 6 7 | |
Отклонение промиса¶
Если промис отклоняется, будет выполнен переход onError с событием { type: 'xstate.error.actor.<id>' }. Данные ошибки доступны в свойстве error события:
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 | |
Если переход onError отсутствует и промис отклонён, ошибка будет выброшена. Однако вы можете обработать все выброшенные ошибки актора, подписавшись с объектом-наблюдателем с функцией error:
1 2 3 | |
Вызов колбэков¶
Вы можете вызвать логику callback-актора следующим образом:
- Настройте логику callback-актора в объекте
actorsвызоваsetup({ actors: { ... } }) - Вызовите логику callback-актора по имени источника (
src) в свойствеinvokeсостояния
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | |
Читайте логика callback-актора для получения дополнительной информации о callback-акторах.
Вызов Observable¶
Вы можете вызвать логику observable следующим образом:
- Настройте логику observable в объекте
actorsвызоваsetup({ actors: { ... } }) - Вызовите логику observable по имени источника (
src) в свойствеinvokeсостояния
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 | |
Читайте логика observable-актора для получения дополнительной информации об observable-акторах.
Вызов Event Observable¶
Вы можете вызвать Event Observable с помощью создателя логики актора fromEventObservable(...). Логика event observable похожа на логику observable в том, что родительский актор подписывается на event observable; однако генерируемые значения event observable ожидаются как события, которые отправляются вызывающему (родительскому) актору напрямую.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | |
Вызов переходов¶
Вы можете вызвать логику transition-актора следующим образом:
- Настройте логику transition-актора в объекте
actorsвызоваsetup({ actors: { ... } }) - Вызовите логику transition-актора по имени источника (
src) в свойствеinvokeсостояния
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 | |
Читайте логика transition-актора для получения дополнительной информации о transition-акторах.
Вызов автоматов¶
Вы можете вызвать логику актора конечного автомата следующим образом:
- Настройте логику актора конечного автомата в объекте
actorsвызоваsetup({ actors: { ... } }) - Вызовите логику актора конечного автомата по имени источника (
src) в свойствеinvokeсостояния
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 | |
Читайте логика актора конечного автомата для получения дополнительной информации об акторах конечных автоматов.
Отправка ответов¶
Вызванный актор (или порождённый актор) может отвечать другому актору; т.е. он может отправить событие в ответ на событие, отправленное другим актором. Для этого предоставьте ссылку на отправляющего актора как пользовательское свойство объекта отправляемого события. В следующем примере мы используем event.sender, но подойдёт любое имя.
1 2 3 4 5 6 7 8 9 10 11 12 13 | |
В следующем примере автомат 'client' отправляет событие 'CODE' вызванному актору 'auth-server', который затем отвечает событием 'TOKEN' через 1 секунду.
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 | |
Обратите внимание, что по умолчанию sendTo отправляет события анонимно, в этом случае получатель не будет знать источник события.
Примечание
В XState v4 для этой цели использовался создатель действия respond(...). В XState v5 вместо этого используйте sendTo(...).
Несколько акторов¶
Вы можете вызвать несколько акторов, указав каждого в массиве:
1 2 3 4 5 | |
Каждый вызов создаст новый экземпляр этого актора, поэтому даже если src нескольких акторов одинаков (например, someActor выше), будет вызвано несколько экземпляров someActor.
Тестирование¶
Вы можете тестировать вызванных акторов, проверяя, что родительский актор получает ожидаемые события от вызванного актора.
1 2 3 4 5 6 7 8 9 | |
Ссылки на вызванных акторов¶
Акторы могут быть прочитаны из snapshot.children.<actorId>. Возвращаемое значение — объект ActorRef со свойствами:
id- ID актораsend()getSnapshot()
1 2 3 4 5 | |
snapshot.children — это объект ключ-значение, где ключи — это ID актора, а значения — это ActorRef.
Invoke и TypeScript¶
TypeScript
XState v5 требует TypeScript версии 5.0 или выше.
Для лучших результатов используйте последнюю версию TypeScript. Подробнее о XState и TypeScript
Вы должны использовать API setup({ ... }) для правильного вывода типов для логики вызываемого актора.
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 | |
Читайте документацию по настройке конечных автоматов для получения дополнительной информации.
Шпаргалка по invoke¶
Шпаргалка: вызов актора¶
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 | |
Шпаргалка: вызов актора в корне автомата¶
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | |
Шпаргалка: вызов нескольких акторов как массив¶
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | |