# 延迟事件和转换
¥Delayed events and transitions
这些 XState v4 文档不再维护
XState v5 现已推出!阅读有关 XState v5 的更多信息 (opens new window)
¥XState v5 is out now! Read more about XState v5 (opens new window)
🆕 在我们的新文档中查找有关 XState 中的延迟(之后)转换 (opens new window) 和 延迟转换的无代码介绍 (opens new window) 的更多信息。
¥🆕 Find more about delayed (after) transitions in XState (opens new window) as well as a no-code introduction to delayed transitions (opens new window) in our new docs.
可以使用状态图以声明方式处理延迟和超时。要了解更多信息,请参阅 状态图简介 中的部分。
¥Delays and timeouts can be handled declaratively with statecharts. To learn more, see the section in our introduction to statecharts.
# 延迟转换
¥Delayed transitions
延迟后可以自动进行转换。这在 after
属性中的状态定义中表示,它将毫秒延迟映射到它们的转换:
¥Transitions can be taken automatically after a delay. This is represented in a state definition in the after
property, which maps millisecond delays to their transitions:
const lightDelayMachine = createMachine({
id: 'lightDelay',
initial: 'green',
states: {
green: {
after: {
// after 1 second, transition to yellow
1000: { target: 'yellow' }
}
},
yellow: {
after: {
// after 0.5 seconds, transition to red
500: { target: 'red' }
}
},
red: {
after: {
// after 2 seconds, transition to green
2000: { target: 'green' }
}
}
}
});
可以采用与在 on: ...
属性上指定延迟转换相同的方式来指定延迟转换。它们可以是明确的:
¥Delayed transitions can be specified in the same way that you specify them on the on: ...
property. They can be explicit:
// ...
states: {
green: {
after: {
1000: { target: 'yellow' }
}
}
}
// ...
延迟转换也可以以单个延迟值为条件:
¥Delayed transitions can also be conditional with regard to a single delay value:
// ...
states: {
green: {
after: {
1000: [
{ target: 'yellow', cond: 'trafficIsLight' },
{ target: 'green' } // reenter 'green' state
]
}
}
}
// ...
或者,延迟转换可以以多次延迟为条件。将进行第一个选定的延迟转换,这将阻止进行后续转换。在以下示例中,如果 'trafficIsLight'
条件为 true
,则不会进行后面的 2000: 'yellow'
转换:
¥Or delayed transitions can be conditional for multiple delays. The first selected delayed transition will be taken, which will prevent later transitions from being taken. In the following example, if the 'trafficIsLight'
condition is true
, then the later 2000: 'yellow'
transition will not be taken:
// ...
states: {
green: {
after: {
1000: { target: 'yellow', cond: 'trafficIsLight' },
2000: { target: 'yellow' } // always transition to 'yellow' after 2 seconds
}
}
}
// ...
条件延迟转换也可以指定为数组:
¥Conditional delayed transitions can also be specified as an array:
// ...
states: {
green: {
after: [
{ delay: 1000, target: 'yellow', cond: 'trafficIsLight' },
{ delay: 2000, target: 'yellow' }
];
}
}
// ...
# 转场时的延迟表达式 4.4+
¥Delay expressions on transitions 4.4+
after: { ... }
属性上指定的延迟转换可以具有动态延迟,由字符串延迟引用指定:
¥Delayed transitions specified on the after: { ... }
property can have dynamic delays, specified either by a string delay reference:
const lightDelayMachine = createMachine(
{
id: 'lightDelay',
initial: 'green',
context: {
trafficLevel: 'low'
},
states: {
green: {
after: {
// after 1 second, transition to yellow
LIGHT_DELAY: { target: 'yellow' }
}
},
yellow: {
after: {
YELLOW_LIGHT_DELAY: { target: 'red' }
}
}
// ...
}
},
{
// String delays configured here
delays: {
LIGHT_DELAY: (context, event) => {
return context.trafficLevel === 'low' ? 1000 : 3000;
},
YELLOW_LIGHT_DELAY: 500 // static value
}
}
);
或者直接通过函数,就像条件延迟转换一样:
¥Or directly by a function, just like conditional delayed transitions:
// ...
green: {
after: [
{
delay: (context, event) => {
return context.trafficLevel === 'low' ? 1000 : 3000;
},
target: 'yellow'
}
]
},
// ...
但是,更喜欢使用字符串延迟引用,就像第一个示例一样,或者在 delay
属性中:
¥However, prefer using string delay references, just like the first example, or in the delay
property:
// ...
green: {
after: [
{
delay: 'LIGHT_DELAY',
target: 'yellow'
}
]
},
// ...
# 延迟事件
¥Delayed events
如果你只想在延迟后发送事件,则可以将 delay
指定为 send(...)
操作创建者的第二个参数中的选项:
¥If you just want to send an event after a delay, you can specify the delay
as an option in the second argument of the send(...)
action creator:
import { actions } from 'xstate';
const { send } = actions;
// action to send the 'TIMER' event after 1 second
const sendTimerAfter1Second = send({ type: 'TIMER' }, { delay: 1000 });
你还可以通过取消这些延迟事件来防止发送这些事件。这是通过 cancel(...)
动作创建器完成的:
¥You can also prevent those delayed events from being sent by canceling them. This is done with the cancel(...)
action creator:
import { actions } from 'xstate';
const { send, cancel } = actions;
// action to send the 'TIMER' event after 1 second
const sendTimerAfter1Second = send(
{ type: 'TIMER' },
{
delay: 1000,
id: 'oneSecondTimer' // give the event a unique ID
}
);
const cancelTimer = cancel('oneSecondTimer'); // pass the ID of event to cancel
const toggleMachine = createMachine({
id: 'toggle',
initial: 'inactive',
states: {
inactive: {
entry: sendTimerAfter1Second,
on: {
TIMER: { target: 'active' },
CANCEL: { actions: cancelTimer }
}
},
active: {}
}
});
// if the CANCEL event is sent before 1 second, the TIMER event will be canceled.
# 延迟表达式 4.3+
¥Delay Expressions 4.3+
delay
选项也可以被计算为延迟表达式,它是一个函数,它接受触发 send()
操作的当前 context
和 event
,并返回解析后的 delay
(以毫秒为单位):
¥The delay
option can also be evaluated as a delay expression, which is a function that takes in the current context
and event
that triggered the send()
action, and returns the resolved delay
(in milliseconds):
const dynamicDelayMachine = createMachine({
id: 'dynamicDelay',
context: {
initialDelay: 1000
},
initial: 'idle',
states: {
idle: {
on: {
ACTIVATE: { target: 'pending' }
}
},
pending: {
entry: send(
{ type: 'FINISH' },
{
// delay determined from custom event.wait property
delay: (context, event) => context.initialDelay + event.wait || 0
}
),
on: {
FINISH: { target: 'finished' }
}
},
finished: { type: 'final' }
}
});
const dynamicDelayService = interpret(dynamicDelayMachine);
dynamicDelayService.subscribe({ complete: () => console.log('done!') });
dynamicDelayService.start();
dynamicDelayService.send({
type: 'ACTIVATE',
// arbitrary property
wait: 2000
});
// after 3000ms (1000 + 2000), console will log:
// => 'done!'
# 解释
¥Interpretation
对于 XState interpreter,延迟操作将使用 nativesetTimeout
和 clearTimeout
函数:
¥With the XState interpreter, delayed actions will use the nativesetTimeout
and clearTimeout
functions:
import { interpret } from 'xstate';
const service = interpret(lightDelayMachine).onTransition((state) =>
console.log(state.value)
);
service.start();
// => 'green'
// (after 1 second)
// => 'yellow'
为了测试,XState 解释器提供了 SimulatedClock
:
¥For testing, the XState interpreter provides a SimulatedClock
:
import { interpret } from 'xstate';
// import { SimulatedClock } from 'xstate/lib/interpreter'; // < 4.6.0
import { SimulatedClock } from 'xstate/lib/SimulatedClock'; // >= 4.6.0
const simulatedClock = new SimulatedClock();
const service = interpret(lightDelayMachine, {
clock: simulatedClock
}).onTransition((state) => console.log(state.value));
service.start();
// => 'green'
// move the SimulatedClock forward by 1 second
simulatedClock.increment(1000);
// => 'yellow'
你可以创建自己的“时钟”以提供给口译员。时钟接口是一个具有两个函数/方法的对象:
¥You can create your own “clock” to provide to the interpreter. The clock interface is an object with two functions/methods:
setTimeout
- 与window.setTimeout(fn, timeout)
相同的参数¥
setTimeout
- same arguments aswindow.setTimeout(fn, timeout)
clearTimeout
- 与window.clearTimeout(id)
相同的参数¥
clearTimeout
- same arguments aswindow.clearTimeout(id)
# 幕后
¥Behind the scenes
after: ...
属性没有给状态图语义引入任何新内容。相反,它会创建如下所示的正常转场:
¥The after: ...
property does not introduce anything new to statechart semantics. Instead, it creates normal transitions that look like this:
// ...
states: {
green: {
entry: [
send({ type: after(1000, 'light.green') }, { delay: 1000 }),
send({ type: after(2000, 'light.green') }, { delay: 2000 })
],
onExit: [
cancel(after(1000, 'light.green')),
cancel(after(2000, 'light.green'))
],
on: {
[after(1000, 'light.green')]: {
target: 'yellow',
cond: 'trafficIsLight'
},
[after(2000, 'light.green')]: {
target: 'yellow'
}
}
}
}
// ...
解释的状态图将在 delay
后对 after(...)
事件进行 send(...)
,除非退出状态节点,这将对那些延迟的 send(...)
事件进行 cancel(...)
。
¥The interpreted statechart will send(...)
the after(...)
events after their delay
, unless the state node is exited, which will cancel(...)
those delayed send(...)
events.