Отмена эффектов
Отмена эффектов в lite-fsm означает не остановку уже запущенной async-операции, а контроль над тем, какие transition-вызовы из effect всё ещё имеют право изменить состояние.
Примеры reducer-ов на этой странице написаны в стиле Immer: они мутируют draft
stateи предполагают, чтоMachineManagerсоздан сimmerMiddleware. Без middleware reducer должен вернуть новый объект{ state, context }.
Проблема асинхронных эффектов
В приложениях, использующих асинхронные операции (HTTP-запросы, таймеры, загрузка данных), могут возникать случаи, когда:
- Пользователь переходит на другую страницу до завершения запроса
- Новый запрос инициируется до завершения предыдущего
- Состояние автомата меняется, делая результат асинхронной операции неактуальным
В таких случаях нужно отделить выполнение async-кода от права этого кода отправить результат обратно в автомат.
Отмена через latest
По умолчанию при смене состояния автомата эффекты не отменяются автоматически. Отмена transition-вызовов из устаревшего запуска включается через createEffect({ type: "latest", ... }).
Важно: при отмене effect сам код уже запущенного effect продолжает выполняться. Отмена блокирует только transition-вызовы внутри устаревшего запуска:
import { createEffect, createMachine } from "@lite-fsm/core";
export const player = createMachine({
config: {
IDLE: {
CHANGE_TRACK: "LOAD_TRACK_DATA",
},
LOAD_TRACK_DATA: {
LOAD_TRACK_DATA_STARTED: "LOAD_TRACK_DATA_PENDING",
},
LOAD_TRACK_DATA_PENDING: {
LOAD_TRACK_DATA_RESOLVE: "SELECT_STREAM_PENDING",
CHANGE_TRACK: "LOAD_TRACK_DATA", // Переход сюда НЕ отменит текущие эффекты по умолчанию
},
SELECT_STREAM_PENDING: {
SELECT_STREAM_RESOLVE: "SET_STREAM_PENDING",
},
SET_STREAM_PENDING: {},
},
initialState: "IDLE",
initialContext: {},
reducer: (state, _action, { nextState }) => {
state.state = nextState;
},
effects: {
LOAD_TRACK_DATA: ({ transition }) => {
transition({ type: "LOAD_TRACK_DATA_STARTED" });
},
LOAD_TRACK_DATA_PENDING: createEffect({
type: "latest", // включает игнорирование старых transition при перезапуске эффекта
effect: async ({ transition }) => {
// Здесь код продолжит выполняться даже после "отмены",
// но вызовы transition из устаревшего запуска будут проигнорированы
await someAsyncOperation();
transition({
type: "LOAD_TRACK_DATA_RESOLVE",
});
},
}),
},
});В этом примере, если автомат находится в LOAD_TRACK_DATA_PENDING и приходит CHANGE_TRACK, transition-вызовы из прежнего запуска будут проигнорированы благодаря type: "latest". Без этой опции результаты всех запусков смогут отправлять события независимо от смены состояния.
Типы эффектов и стратегии отмены
lite-fsm поддерживает две стратегии для обёртки createEffect:
1. Тип “latest”
Право отправлять transition остаётся только у последнего запуска. Transition-вызовы из предыдущих запусков игнорируются:
const searchEffect = createEffect({
type: "latest", // включает игнорирование transition-вызовов из прежних запусков
effect: async ({ action, api, transition }) => {
const results = await api.search(action.payload.query);
transition({ type: "SEARCH_RESOLVE", payload: { results } });
},
});2. Тип “every”
Все запуски сохраняют право отправлять transition. Это поведение используется по умолчанию:
const logEffect = createEffect({
type: "every", // можно не указывать — это значение по умолчанию
effect: async ({ action, analytics }) => {
await analytics.logEvent(action);
},
});