Action
Класс, представляющий действие (декорированный метод) с возможностями управления состоянием.
class Action<T extends object, Args extends any[] = unknown[]>Дженерики:
T extends object- Тип модели, которая содержит действиеArgs extends any[]- Тип аргументов метода действия
Конструктор
constructor(
protected _model: T,
protected actionFunction: OriginalMethodWrapper<Args>,
protected ownerGetter: () => Model<T>,
protected setStateCb: (
action: ActionLike<T, Args>,
oldState: ActionStateName,
newState: ActionStateName,
) => void,
protected _validateArgs: (
action: ActionLike<T, Args>,
...args: Args
) => Error[],
)Создаёт новый экземпляр Action. Экземпляр не реактивен. Конструктор не вызывается напрямую. Для создания реактивных экземпляров используется статический метод Action.create().
Параметры:
_model-T- Экземпляр модели, содержащий метод действияactionFunction-OriginalMethodWrapper<Args>- оригинальный метод с флагомAction.actionFlag. Флаг устанавливается декоратором@actionownerGetter-() => Model<T>- Функция, возвращающая экземпляр Model, которому принадлежит действиеsetStateCb-(action, oldState, newState) => void- Callback для обновления состояния действия в модели при изменении состояния_validateArgs-(action, ...args) => Error[]- Функция для валидации аргументов действия
Выбрасывает:
ActionInternalError- Если модель не содержит метод с именем изactionFunction.nameActionInternalError- Если метод не является действием (не имеет флагаAction.actionFlag)
Поведение:
- Извлекает имя метода из
actionFunction.name - Проверяет, что модель содержит метод с таким именем
- Проверяет, что метод является действием (имеет флаг
Action.actionFlag) - Сохраняет имя в свойство
name
Статические свойства
Action.possibleState
static readonly possibleState: {
readonly pending: 'pending'
readonly error: 'error'
readonly lock: 'lock'
readonly ready: 'ready'
readonly abort: 'abort'
}Объект, содержащий все возможные состояния действия.
Тип:
Readonly<Record<ActionStateName, ActionStateName>>
Статические методы
Action.create
static create<T extends ProtoModel, Args extends unknown[] = unknown[]>(
model: T,
actionFunction: OriginalMethodWrapper<Args>,
ownerGetter: () => Model<T>,
setStateCb: (
action: ActionLike<T, Args>,
oldState: ActionStateName,
newState: ActionStateName,
) => void,
validateArgs: (
action: ActionLike<T, Args>,
...args: Args
) => Error[],
): ActionLike<T, Args>Фабричный метод для создания нового реактивного экземпляра Action. Внутренне вызывает constructor и оборачивает результат в shallowReactive() для реактивности.
Параметры:
Параметры полностью совпадают с параметрами конструктора. См. Конструктор.
Возвращает:
ActionLike<T, Args> - Реактивный (shallowReactive) экземпляр Action
Выбрасывает:
ActionInternalError- Если модель не содержит метод с именем изactionFunctionили метод не является действием
Свойства экземпляра
name
readonly name: stringИмя действия (имя метода).
owner
readonly owner: Model<T>Экземпляр модели, которому принадлежит это действие. Внутри вызывает функцию ownerGetter, которая передана в конструкторе
state
readonly state: ActionStateNameТекущее состояние действия.
Возможные значения:
'pending' | 'error' | 'lock' | 'ready' | 'abort'
abortController
readonly abortController: AbortController | nullЭкземпляр AbortController для управления прерыванием выполнения действия.
Значение:
AbortController- если действие находится в состоянииpendingnull- во всех остальных состояниях
args
readonly args: Args | never[]Аргументы, переданные при последнем выполнении действия.
Значение:
Args- если действие было выполнено хотя бы разnever[](пустой массив) - если действие ещё не выполнялось
promise
readonly promise: Promise<void> | nullПромис текущего выполнения действия.
Значение:
Promise<void>- если действие находится в состоянииpendingnull- во всех остальных состояниях
error
readonly error: ActionError | nullЭкземпляр ActionError, содержащий информацию об ошибке выполнения.
Значение:
ActionError- если действие находится в состоянииerrornull- во всех остальных состояниях
abortReason
readonly abortReason: unknownПричина прерывания действия.
Значение:
- Причина прерывания - если действие находится в состоянии
abort null- во всех остальных состояниях
isPending
readonly isPending: booleanПроверяет, находится ли действие в состоянии pending. Эквивалентно: state === 'pending'.
isError
readonly isError: booleanПроверяет, находится ли действие в состоянии error. Эквивалентно: state === 'error'.
isReady
readonly isReady: booleanПроверяет, находится ли действие в состоянии ready. Эквивалентно: state === 'ready'.
isLock
readonly isLock: booleanПроверяет, находится ли действие в состоянии lock. Эквивалентно: state === 'lock'.
isAbort
readonly isAbort: booleanПроверяет, находится ли действие в состоянии abort. Эквивалентно: state === 'abort'.
Методы экземпляра
is
is(...args: ActionStateName[]): booleanПроверяет, находится ли действие в любом из указанных состояний.
Параметры:
...args-ActionStateName[]- Имена состояний для проверки
Возвращает:
boolean - true если действие находится в любом из указанных состояний, иначе false
Пример:
if (action.is('pending', 'lock')) {
// Действие либо pending, либо заблокировано
}validate
validate(...args: Args): Error[]Валидирует аргументы действия. Внутри вызывает функцию _validateArgs преданную как аргумент конструктора. Это самостоятельный метод, не используется в exec.
Удобно использовать для проверки пользовательского ввода, что бы получить ошибки без выполнения exec.
Параметры:
...args-Args- Аргументы для валидации
Возвращает:
Error[] - Массив ошибок валидации. Пустой массив, если все аргументы валидны.
Пример:
const errors = action.validate('arg1', 'arg2')
if (errors.length > 0) {
// Обработать ошибки валидации
}exec
exec(...args: Args): Promise<void>Выполняет действие и переводит его в состояние pending. Если метод действия возвращает Promise, ожидает его завершения и обновляет состояние соответственно.
Параметры:
...args-Args- Аргументы для передачи в метод действия- Если последний аргумент является
AbortController, он будет использован для управления прерыванием - Иначе будет создан новый
AbortControllerи добавлен в конец аргументов
- Если последний аргумент является
Возвращает:
Promise<void> - Промис, который разрешается при успешном завершении действия или отклоняется при ошибке/прерывании.
Выбрасывает:
ActionStatusConflictError- Если действие уже находится в состоянииlockилиpendingActionInternalError- При внутренних ошибках (не перехватывается)RangeError,ReferenceError,SyntaxError,TypeError,URIError,EvalError- Перебрасываются как есть (не перехватываются)ActionUnexpectedAbortError- Если действие было прервано, но не находилось в состоянииpendingилиlock
Поведение:
- Проверяет, что действие не находится в состоянии
lockилиpending - Устанавливает состояние действия в
pendingперед выполнением (для предотвращения рекурсивных вызовов) - Сохраняет аргументы в
_args - Вызывает оригинальный метод с аргументами (добавляя
AbortControllerпри необходимости) - Если метод возвращает не-Promise:
- Немедленно устанавливает состояние в
ready - Возвращает разрешённый промис
- Немедленно устанавливает состояние в
- Если метод возвращает Promise:
- При успехе: устанавливает состояние в
ready - При ошибке: оборачивает ошибку в
ActionErrorи устанавливает состояние вerror - При прерывании: устанавливает состояние в
abortилиlock(если прервано блокировкой)
- При успехе: устанавливает состояние в
Важно: Ошибки выполнения оборачиваются в ActionError и сохраняются в состоянии error. Внешний try/catch не перехватит эти ошибки. Для обработки ошибок используйте проверку action.error после ожидания промиса или watcher по состоянию действия.
Пример:
await action.exec('arg1', 'arg2')
if (action.error?.cause) {
handleError(action.error.cause)
return
}
// или с watcher:
watch(
() => model.someAction.error,
(error) => {
if (!error) return
handleError(error.cause)
}
)abort
abort(reason?: unknown): Promise<void>Прерывает текущее выполнение действия, если оно находится в состоянии pending. Если действие не находится в состоянии pending, возвращает разрешённый промис без выполнения каких-либо действий.
Параметры:
reason-unknown(опционально) - Причина прерывания. Будет доступна черезabortReason.
Возвращает:
Promise<void> - Тот же промис, что был возвращён методом exec(). Промис будет отклонён с причиной прерывания.
Пример:
const promise = action.exec()
action.abort('Пользователь отменил')
await promise.catch(() => {
// Обработать прерывание
console.log(action.abortReason) // 'Пользователь отменил'
})lock
lock(): Promise<void>Блокирует действие, предотвращая дальнейшее выполнение. Если действие находится в состоянии pending, оно будет прервано с причиной Action.abortedByLock.
Возвращает:
Promise<void> - Промис, который разрешается при применении блокировки.
Поведение:
- Если действие в состоянии
pending: прерывает выполнение с причинойAction.abortedByLock, затем устанавливает состояние вlock - Если действие не в состоянии
pending: немедленно устанавливает состояние вlock
Пример:
await action.lock()
// Действие теперь заблокировано и не может быть выполнено
// Попытка вызвать exec() выбросит ActionStatusConflictErrorunlock
unlock(): thisРазблокирует действие и устанавливает его в состояние ready.
Возвращает:
this - Экземпляр действия (для цепочки вызовов).
Выбрасывает:
ActionStatusConflictError- Если действие не находится в состоянииlock
Пример:
action.unlock()
// Действие теперь готово и может быть выполненоresetError
resetError(): thisСбрасывает состояние ошибки и устанавливает действие в состояние ready.
Возвращает:
this - Экземпляр действия (для цепочки вызовов).
Выбрасывает:
ActionStatusConflictError- Если действие не находится в состоянииerror(т.е. нет ошибки для сброса)
Пример:
if (action.error) {
action.resetError()
// Действие теперь готово и может быть выполнено снова
}toString
toString(): stringВозвращает имя действия в виде строки.
Возвращает:
string - Имя действия (имя метода).
Пример:
console.log(action.toString()) // 'someAction'
console.log(String(action)) // 'someAction'Интерфейсы
Интерфейс ActionLike (публичный контракт Action) вынесен в отдельный справочник: Интерфейсы и типы.
