# 解释机器
¥Interpreting Machines
这些 XState v4 文档不再维护
XState v5 现已推出!阅读有关 XState v5 的更多信息 (opens new window)
¥XState v5 is out now! Read more about XState v5 (opens new window)
🆕 在我们的新文档中查找有关 使用 XState 创建 Actor (opens new window) 的更多信息。
¥🆕 Find more about creating actors using XState (opens new window) in our new docs.
虽然具有纯 .transition()
函数的状态机/状态图对于灵活性、纯度和可测试性很有用,但为了使其在实际应用中发挥作用,需要满足以下条件:
¥While a state machine/statechart with a pure .transition()
function is useful for flexibility, purity, and testability, in order for it to have any use in a real-life application, something needs to:
跟踪当前状态并坚持下去
¥Keep track of the current state, and persist it
执行副作用
¥Execute side-effects
处理延迟的转换和事件
¥Handle delayed transitions and events
与外部服务通信
¥Communicate with external services
解释器负责解释状态机/状态图并执行上述所有操作 - 即在运行时环境中解析并执行。状态图的解释运行实例称为服务。
¥The interpreter is responsible for interpreting the state machine/statechart and doing all of the above - that is, parsing and executing it in a runtime environment. An interpreted, running instance of a statechart is called a service.
# 解释器 4.0+
¥Interpreter 4.0+
提供了一个可选的解释器,你可以使用它来运行状态图。解释器处理:
¥An optional interpreter is provided that you can use to run your statecharts. The interpreter handles:
状态转换
¥State transitions
执行操作(副作用)
¥Executing actions (side-effects)
延迟且取消的活动
¥Delayed events with cancellation
活动(持续的行动)
¥Activities (ongoing actions)
调用/生成子状态图服务
¥Invoking/spawning child statechart services
支持状态转换、上下文更改、事件等的多个监听器。
¥Support for multiple listeners for state transitions, context changes, events, etc.
和更多!
¥And more!
import { createMachine, interpret } from 'xstate';
const machine = createMachine(/* machine config */);
// Interpret the machine, and add a listener for whenever a transition occurs.
const service = interpret(machine).onTransition((state) => {
console.log(state.value);
});
// Start the service
service.start();
// Send events
service.send({ type: 'SOME_EVENT' });
// Stop the service when you are no longer using it.
service.stop();
# 发送事件
¥Sending Events
通过调用 service.send(event)
将事件发送到正在运行的服务。可以通过 3 种方式发送事件:
¥Events are sent to a running service by calling service.send(event)
. There are 3 ways an event can be sent:
service.start();
service.send({ type: 'CLICK', x: 40, y: 21 });
作为事件对象(例如,
.send({ type: 'CLICK', x: 40, y: 21 })
)¥As an event object (e.g.,
.send({ type: 'CLICK', x: 40, y: 21 })
)事件对象必须具有
type: ...
字符串属性。¥The event object must have a
type: ...
string property.
警告
如果服务未初始化(即,如果尚未调用 service.start()
),事件将推迟到服务启动为止。这意味着事件在调用 service.start()
之前不会被处理,然后它们将被顺序处理。
¥If the service is not initialized (that is, if service.start()
wasn't called yet), events will be deferred until the service is started. This means that the events won't be processed until service.start()
is called, and then they will all be sequentially processed.
可以通过在 服务选项 中设置 { deferEvents: false }
来更改此行为。当 deferEvents
为 false
时,向未初始化的服务发送事件将引发错误。
¥This behavior can be changed by setting { deferEvents: false }
in the service options. When deferEvents
is false
, sending an event to an uninitialized service will throw an error.
# 批量事件
¥Batched Events
通过使用事件数组调用 service.send(events)
,可以将多个事件作为一组或 "batch" 发送到正在运行的服务:
¥Multiple events can be sent as a group, or "batch", to a running service by calling service.send(events)
with an array of events:
service.send([
// String events
'CLICK',
'CLICK',
'ANOTHER_EVENT',
// Event objects
{ type: 'CLICK', x: 40, y: 21 },
{ type: 'KEYDOWN', key: 'Escape' }
]);
这将立即安排所有批处理事件按顺序处理。由于每个事件都会导致可能有要执行的操作的状态转换,因此中间状态中的操作将被推迟,直到处理完所有事件为止,然后以创建它们时的状态(而不是最终状态)执行它们。
¥This will immediately schedule all batched events to be processed sequentially. Since each event causes a state transition that might have actions to execute, actions in intermediate states are deferred until all events are processed, and then they are executed with the state they were created in (not the end state).
这意味着最终状态(处理所有事件后)将具有一个 .actions
数组,其中包含中间状态的所有累积操作。这些动作中的每一个都将绑定到它们各自的中间状态。
¥This means that the end state (after all events are processed) will have an .actions
array of all of the accumulated actions from the intermediate states. Each of these actions will be bound to their respective intermediate states.
警告
只有一种状态 - 最终状态(即处理所有事件后的结果状态) - 将被发送到 .onTransition(...)
监听器。这使得批量事件成为性能优化的方法。
¥Only one state -- the end state (i.e., the resulting state after all events are processed) -- will be sent to the .onTransition(...)
listener(s). This makes batched events an optimized approach for performance.
提示
批量事件对于 事件溯源 (opens new window) 方法很有用。可以存储事件日志,然后通过将批量事件发送到服务以达到相同状态来重播。
¥Batched events are useful for event sourcing (opens new window) approaches. A log of events can be stored and later replayed by sending the batched events to a service to arrive at the same state.
# 转场
¥Transitions
状态转换的监听器通过 .onTransition(...)
方法注册,该方法采用状态监听器。每次状态转换(包括初始状态)发生时都会调用状态监听器,当前为 state
实例:
¥Listeners for state transitions are registered via the .onTransition(...)
method, which takes a state listener. State listeners are called every time a state transition (including the initial state) happens, with the current state
instance:
// Interpret the machine
const service = interpret(machine);
// Add a state listener, which is called whenever a state transition occurs.
service.onTransition((state) => {
console.log(state.value);
});
service.start();
提示
如果你只想在状态更改时(即,当 state.value
更改、state.context
更改或有新的 state.actions
时)调用 .onTransition(...)
处理程序,请使用 state.changed
(opens new window):
¥If you only want the .onTransition(...)
handler(s) to be called when the state changes (that is, when the state.value
changes, the state.context
changes, or there are new state.actions
), use state.changed
(opens new window):
service.onTransition((state) => {
if (state.changed) {
console.log(state.value);
}
});
提示
.onTransition()
回调不会在无事件 ("always") 转换或其他微步之间运行。它仅在宏步骤上运行。微步是宏步之间的中间转场。
¥The .onTransition()
callback will not run between eventless ("always") transitions or other microsteps. It only runs on macrosteps.
Microsteps are the intermediate transitions between macrosteps.
# 启动和停止
¥Starting and Stopping
可以使用 .start()
和 .stop()
来初始化(即启动)和停止服务。调用 .start()
将立即将服务转换到其初始状态。调用 .stop()
将从服务中删除所有监听器,并执行任何监听器清理(如果适用)。
¥The service can be initialized (i.e., started) and stopped with .start()
and .stop()
. Calling .start()
will immediately transition the service to its initial state. Calling .stop()
will remove all listeners from the service, and do any listener cleanup, if applicable.
const service = interpret(machine);
// Start the machine
service.start();
// Stop the machine
service.stop();
// Restart the machine
service.start();
通过将 state
传递到 service.start(state)
可以从特定的 state 启动服务。当从先前保存的状态重新水合服务时,这非常有用。
¥Services can be started from a specific state by passing the state
into service.start(state)
. This is useful when rehydrating the service from a previously saved state.
// Starts the service from the specified state,
// instead of from the machine's initial state.
service.start(previousState);
# 执行动作
¥Executing Actions
默认情况下,当状态转换时立即执行 作用(副作用)。这可以通过设置 { execute: false }
选项进行配置(参见示例)。state
上指定的每个操作对象可能有一个 .exec
属性,该属性通过状态的 context
和 event
对象来调用。
¥Actions (side-effects) are, by default, executed immediately when the state transitions. This is configurable by setting the { execute: false }
option (see example). Each action object specified on the state
might have an .exec
property, which is called with the state's context
and event
object.
可以通过调用 service.execute(state)
来手动执行操作。当你想要控制操作的执行时间时,这非常有用:
¥Actions can be executed manually by calling service.execute(state)
. This is useful when you want to control when actions are executed:
const service = interpret(machine, {
execute: false // do not execute actions on state transitions
});
service.onTransition((state) => {
// execute actions on next animation frame
// instead of immediately
requestAnimationFrame(() => service.execute(state));
});
service.start();
# waitFor
许多后端代码依赖于短期运行的进程,例如后端函数。在无服务器环境中尤其如此,其中代码需要尽快启动和关闭。
¥Lots of backend code relies on short-running processes, such as backend functions. This is especially true in serverless contexts, where code needs to boot up and shut down as fast as possible.
很多此类代码都依赖于 async
函数:
¥A lot of this type of code relies on async
functions:
const myFunc = async () => {};
用于异步函数的最佳模式是 waitFor
,它使你能够对处于某种状态的状态机进行 await
。
¥The best pattern to use for async functions is waitFor
, which gives you the ability to await
a state machine being in a certain state.
import { interpret, createMachine } from 'xstate';
import { waitFor } from 'xstate/lib/waitFor';
const machine = createMachine({
initial: 'pending',
states: {
pending: {
after: {
3000: {
target: 'done'
}
}
},
done: {}
}
});
const myFunc = async () => {
const actor = interpret(machine).start();
const doneState = await waitFor(actor, (state) => state.matches('done'));
console.log(doneState.value); // 'done'
};
在上面的示例中,机器在进入 done
状态之前等待三秒钟 - 此时,await
将得到解决,程序将继续进行。
¥In the example above, the machine waits for three seconds before moving on to its done
state - at which point the await
will resolve and the program will move on.
默认情况下,如果未达到所需状态,waitFor
将在 10 秒后抛出错误。你可以通过在选项中传递 timeout
来自定义此超时:
¥By default, waitFor
will throw an error after 10 seconds if the desired state is not reached. You can customize this timeout by passing timeout
in the options:
const myFunc = async () => {
const actor = interpret(machine).start();
const doneState = await waitFor(actor, (state) => state.matches('done'), {
// 20 seconds in ms
timeout: 20_000
});
};
如果 waitFor
达到你选择的最终状态以外的状态,它也会抛出错误。有关最终状态的更多信息,点击这里。
¥waitFor
will also throw an error if it reaches a final state other than the one you chose. For more information on final states, click here.
# 选项
¥Options
以下选项可以作为第二个参数 (interpret(machine, options)
) 传递到解释器中:
¥The following options can be passed into the interpreter as the 2nd argument (interpret(machine, options)
):
execute
(布尔值) - 表示状态转换时是否应执行状态操作。默认为true
。¥
execute
(boolean) - Signifies whether state actions should be executed upon transition. Defaults totrue
.有关自定义此行为的信息,请参阅 执行动作。
¥See Executing Actions for customizing this behavior.
deferEvents
(布尔值)<徽章文本="4.4+"/> - 表示发送到未初始化服务(即在调用service.start()
之前)的事件是否应推迟到服务初始化为止。默认为true
。¥
deferEvents
(boolean) 4.4+ - Signifies whether events sent to an uninitialized service (i.e., prior to callingservice.start()
) should be deferred until the service is initialized. Defaults totrue
.如果是
false
,发送到未初始化服务的事件将引发错误。¥If
false
, events sent to an uninitialized service will throw an error.
devTools
(布尔值) - 表示事件是否应发送到 Redux DevTools 扩展 (opens new window)。默认为false
。¥
devTools
(boolean) - Signifies whether events should be sent to the Redux DevTools extension (opens new window). Defaults tofalse
.logger
- 指定用于log(...)
操作的日志器。默认为原生console.log
方法。¥
logger
- Specifies the logger to be used forlog(...)
actions. Defaults to the nativeconsole.log
method.clock
- 指定 用于延迟操作的时钟接口。默认为原生setTimeout
和clearTimeout
函数。¥
clock
- Specifies the clock interface for delayed actions. Defaults to the nativesetTimeout
andclearTimeout
functions.
# 定制解释器
¥Custom Interpreters
你可以使用任何解释器(或创建你自己的解释器)来运行状态机/状态图。下面是一个最小实现示例,演示了解释的灵活性(尽管有大量样板文件):
¥You may use any interpreter (or create your own) to run your state machine/statechart. Here's an example minimal implementation that demonstrates how flexible interpretation can be (despite the amount of boilerplate):
const machine = createMachine(/* machine config */);
// Keep track of the current state, and start
// with the initial state
let currentState = machine.initialState;
// Keep track of the listeners
const listeners = new Set();
// Have a way of sending/dispatching events
function send(event) {
// Remember: machine.transition() is a pure function
currentState = machine.transition(currentState, event);
// Get the side-effect actions to execute
const { actions } = currentState;
actions.forEach((action) => {
// If the action is executable, execute it
typeof action.exec === 'function' && action.exec();
});
// Notify the listeners
listeners.forEach((listener) => listener(currentState));
}
function listen(listener) {
listeners.add(listener);
}
function unlisten(listener) {
listeners.delete(listener);
}
// Now you can listen and send events to update state
listen((state) => {
console.log(state.value);
});
send('SOME_EVENT');
# 注意
¥Notes
从 4.3+(即
import { interpret } from 'xstate'
)开始,interpret
函数直接从xstate
导出。对于之前的版本,它是从'xstate/lib/interpreter'
导入的。¥The
interpret
function is exported directly fromxstate
since 4.3+ (i.e.,import { interpret } from 'xstate'
). For prior versions, it is imported from'xstate/lib/interpreter'
.大多数解释器方法都可以链接:
¥Most interpreter methods can be chained:
const service = interpret(machine)
.onTransition((state) => console.log(state))
.onDone(() => console.log('done'))
.start(); // returns started service
不要直接从操作中调用
service.send(...)
。这阻碍了测试、可视化和分析。相反,使用invoke
。¥Do not call
service.send(...)
directly from actions. This impedes testing, visualization, and analysis. Instead, useinvoke
.