You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: docs/async.md
+61-14Lines changed: 61 additions & 14 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -4,21 +4,61 @@
4
4
Support for async code was added!
5
5
```
6
6
7
-
The {ref}`StateMachine` has full async suport. You can write async {ref}`actions`, {ref}`guards` and {ref}`event` triggers.
7
+
The {ref}`StateMachine` fully supports asynchronous code. You can write async {ref}`actions`, {ref}`guards`, and {ref}`event` triggers, while maintaining the same external API for both synchronous and asynchronous codebases.
8
+
9
+
This is achieved through a new concept called "engine," an internal strategy pattern abstraction that manages transitions and callbacks.
10
+
11
+
There are two engines:
12
+
13
+
SyncEngine
14
+
: Activated if there are no async callbacks. All code runs exactly as it did before version 2.3.0.
15
+
16
+
AsyncEngine
17
+
: Activated if there is at least one async callback. The code runs asynchronously and requires a running event loop, which it will create if none exists.
18
+
19
+
These engines are internal and are activated automatically by inspecting the registered callbacks in the following scenarios:
20
+
21
+
22
+
```{list-table} Sync vs async engines
23
+
:widths: 15 10 25 10 10
24
+
:header-rows: 1
25
+
26
+
* - Outer scope
27
+
- Async callbacks?
28
+
- Engine
29
+
- Creates internal loop
30
+
- Reuses external loop
31
+
* - Sync
32
+
- No
33
+
- Sync
34
+
- No
35
+
- No
36
+
* - Sync
37
+
- Yes
38
+
- Async
39
+
- Yes
40
+
- No
41
+
* - Async
42
+
- No
43
+
- Sync
44
+
- No
45
+
- No
46
+
* - Async
47
+
- Yes
48
+
- Async
49
+
- No
50
+
- Yes
51
+
52
+
```
8
53
9
-
Keeping the same external API do interact both on sync or async codebases.
10
54
11
55
```{note}
12
-
All the handlers will run on the same thread they're called. So it's not recommended to mix sync with async code unless
13
-
you know what you're doing.
56
+
All handlers will run on the same thread they are called. Therefore, mixing synchronous and asynchronous code is not recommended unless you are confident in your implementation.
14
57
```
15
58
16
59
## Asynchronous Support
17
60
18
-
We support native coroutine using asyncio, enabling seamless integration with asynchronous code.
19
-
There's no change on the public API of the library to work on async codebases.
20
-
21
-
One requirement is that when running on an async code, you must manually await for the {ref}`initial state activation` to be able to check the current state.
61
+
We support native coroutine callbacks using asyncio, enabling seamless integration with asynchronous code. There is no change in the public API of the library to work with asynchronous codebases.
22
62
23
63
24
64
```{seealso}
@@ -49,10 +89,10 @@ Final
49
89
50
90
```
51
91
52
-
## Sync codebase with async handlers
92
+
## Sync codebase with async callbacks
93
+
94
+
The same state machine can be executed in a synchronous codebase, even if it contains async callbacks. The callbacks will be awaited using `asyncio.get_event_loop()` if needed.
53
95
54
-
The same state machine can be executed on a sync codebase, even if it contains async handlers. The handlers will be
55
-
awaited on an `asyncio.get_event_loop()` if needed.
56
96
57
97
```py
58
98
>>> sm = AsyncStateMachine()
@@ -68,9 +108,12 @@ Final
68
108
(initial state activation)=
69
109
## Initial State Activation for Async Code
70
110
71
-
When working with asynchronous state machines from async code, users must manually [activate initial state](statemachine.StateMachine.activate_initial_state) to be able to check the current state. This change ensures proper state initialization and
72
-
execution flow given that Python don't allow awaiting at class initalization time and the initial state activation
73
-
may contain async callbacks that must be awaited.
111
+
112
+
If you perform checks against the `current_state`, like a loop `while sm.current_state.is_final:`, then on async code you must manually
113
+
await for the [activate initial state](statemachine.StateMachine.activate_initial_state) to be able to check the current state.
114
+
115
+
If you don't do any check for current state externally, just ignore this as the initial state is activated automatically before the first event trigger is handled.
116
+
74
117
75
118
```py
76
119
>>>asyncdefinitialize_sm():
@@ -83,3 +126,7 @@ may contain async callbacks that must be awaited.
83
126
Initial
84
127
85
128
```
129
+
130
+
```{hint}
131
+
This manual initial state activation on async is because Python don't allow awaiting at class initalization time and the initial state activation may contain async callbacks that must be awaited.
0 commit comments