|
1 | 1 | ---
|
2 |
| -title: Using Signals and Cancellation |
| 2 | +title: Using Cancellation Signals |
3 | 3 | order: 2
|
4 | 4 | ---
|
5 | 5 |
|
6 | 6 | Long running tasks, asynchronous tasks, and operations that have a _set up_ and _tear down_ phase, can make use of a
|
7 |
| -concept called _cancellation_. If you've used other programming languages you might be familiar with objects like |
8 |
| -`CancellationToken` (or [Go's `context`](https://pkg.go.dev/context)) - JavaScript's equivalent is `AbortSignal` which |
9 |
| -can be operated with an `AbortController`. |
| 7 | +concept called _cancellation signals_. If you've used other programming languages you might be familiar with objects |
| 8 | +like `CancellationToken` (or [Go's `context`](https://pkg.go.dev/context)) - JavaScript's equivalent is `AbortSignal` |
| 9 | +which can be operated with an `AbortController`. |
10 | 10 |
|
11 | 11 | The `AbortController` & `AbortSignal` APIs can help manage operational "life time". Some examples:
|
12 | 12 |
|
13 | 13 | - Long running or asynchronous tasks, for example timers or network fetches
|
14 | 14 | - Overlapping operations, for example cancelling in-flight network fetches to replace them with newer ones
|
15 | 15 | - Set up and tear down, for example a Web Components `connectedCallback` and `disconnectedCallback`
|
16 | 16 |
|
17 |
| -## Signal Controller Pattern |
| 17 | +## AbortSignal AbortController Pattern |
18 | 18 |
|
19 |
| -_Signals_ get given to APIs so they know when to abort. Signals are created by _controllers_ (`new AbortSignal()` will |
20 |
| -throw an error). _Controllers_ allow you to make the decision of when a Signal changes. Creating an `AbortController` |
21 |
| -will also create a new _Signal_ accessible via `.signal`. Code that has access to the _controller_ can determine when it |
22 |
| -should be aborted (by calling `.abort()`), while code that has access to the _signal_ can be notified of the abort. To |
23 |
| -make a new `AbortController`, call `new AbortContoller()`. The constructor takes no arguments. |
| 19 | +_Cancellation Signals_ get given to APIs so they know when to abort. An `AbortSignal` is created by a _controller_ |
| 20 | +(`new AbortSignal()` will throw an error). _Controllers_ allow you to make the decision of when a _Cancellation Signal_ |
| 21 | +changes. Creating an `AbortController` will also create a new `AbortSignal`, accessible via `.signal`. Code that has |
| 22 | +access to the _controller_ can determine when it should be aborted (by calling `.abort()`), while code that has access |
| 23 | +to the _signal_ can be notified of the abort. To make a new `AbortController`, call `new AbortContoller()`. The |
| 24 | +constructor takes no arguments. |
24 | 25 |
|
25 |
| -A _signals_ `.aborted` property will be `true` if the signal has been aborted - you can periodically check that to stop |
26 |
| -any work that is about to be done. `AbortSignal` is also an `EventTarget` - it emits an `abort` event which you can |
| 26 | +An `AbortSignal`s `.aborted` property will be `true` if the signal has been aborted - you can periodically check that to |
| 27 | +stop any work that is about to be done. `AbortSignal` is also an `EventTarget` - it emits an `abort` event which you can |
27 | 28 | listen to and invoke your tear down.
|
28 | 29 |
|
29 | 30 | You can also create some basic _controller-free signals_ that follow some common patterns. For example
|
30 |
| -`AbortSignal.timeout(1000)` will create a _signal_ that aborts after 1000ms. These _controller-free signals_ cannot be |
31 |
| -manually aborted. However, you can _combine_ controller-free and controllable signals with |
| 31 | +`AbortSignal.timeout(1000)` will create a _cancellation signal_ that aborts after 1000ms. These _controller-free |
| 32 | +signals_ cannot be manually aborted. However, you can _combine_ controller-free and controllable signals with |
32 | 33 | `AbortSignal.any([...signals])`.
|
33 | 34 |
|
34 |
| -## Using Signals internally manage your private APIs |
| 35 | +## Using Cancellation Signals internally manage your private APIs |
35 | 36 |
|
36 |
| -_Signals_ can be used to manage internal state that you might have. You can create an `AbortController` as part of your |
37 |
| -private state, and make use of _signals_ to control behavior. Consumers of your component won't pass these signals to |
38 |
| -you, instead you can use them to track a tasks state internally. |
| 37 | +_Cancellation Signals_ can be used to manage internal state that you might have. You can create an `AbortController` as |
| 38 | +part of your private state, and make use of _signals_ to control behavior. Consumers of your component won't pass these |
| 39 | +signals to you, instead you can use them to track a tasks state internally. |
39 | 40 |
|
40 | 41 | A component with `start()` and `stop()` functions can make the `stop()` function abort the controller, and the `start()`
|
41 | 42 | function create the controller, while checking if the signal has been aborted during an asynchronous loop like so:
|
@@ -82,14 +83,15 @@ class StopWatchElement extends HTMLElement {
|
82 | 83 | }
|
83 | 84 | ```
|
84 | 85 |
|
85 |
| -## Using Signals in your own public APIs |
| 86 | +## Using Cancellation Signals in your own public APIs |
86 | 87 |
|
87 | 88 | If you can use a signal as part of your internal state, it might be simpler to provide it as part of the public API. If
|
88 |
| -you are considering using _signals_ in a public API, it's a good idea to make them an optional part of your API as they |
89 |
| -won't always be _needed_. |
| 89 | +you are considering using _cancellation signals_ in a public API, it's a good idea to make them an optional part of your |
| 90 | +API as they won't always be _needed_. |
90 | 91 |
|
91 |
| -A component using _signals_ no longer needs separate start & stop methods, instead combining into one and relying on the |
92 |
| -signal to know when to stop. This can often simplify code as there is no need to track state across different methods. |
| 92 | +A component using _cancellation signals_ no longer needs separate start & stop methods, instead combining into one and |
| 93 | +relying on the signal to know when to stop. This can often simplify code as there is no need to track state across |
| 94 | +different methods. |
93 | 95 |
|
94 | 96 | ```js
|
95 | 97 | class StopWatchElement extends HTMLElement {
|
@@ -120,14 +122,15 @@ class StopWatchElement extends HTMLElement {
|
120 | 122 | }
|
121 | 123 | ```
|
122 | 124 |
|
123 |
| -## Combining multiple Signals |
| 125 | +## Combining multiple Cancellation Signals |
124 | 126 |
|
125 |
| -It's possible to combine multiple sources of signals - for example combining internal and external signals to allow for |
126 |
| -multiple flavors of API. Two or more signals can be combined into one using `AbortSignal.any()`, which creates a _new |
127 |
| -signal_ that aborts when any of the given _signals_ abort. It's similar to `Promise.any()`, but for Signals. |
| 127 | +It's possible to combine multiple sources of _cancellation signals_ - for example combining internal and external |
| 128 | +_cancellation signals_ to allow for multiple flavors of API. Two or more _cancellation signals_ can be combined into one |
| 129 | +using `AbortSignal.any()`, which creates a _new signal_ that aborts when any of the given _cancellation signals_ abort. |
| 130 | +It's similar to `Promise.any()`, but for `AbortSignal`. |
128 | 131 |
|
129 |
| -A component can provide the more traditional `start()` and `stop()` APIs, as well allowing signals to be passed via |
130 |
| -`start({ signal })`. Making use of internal and external signals, with `AbortSignal.any()`: |
| 132 | +A component can provide the more traditional `start()` and `stop()` APIs, as well allowing _cancellation signals_ to be |
| 133 | +passed via `start({ signal })`. Making use of internal and external _cancellation signals_, with `AbortSignal.any()`: |
131 | 134 |
|
132 | 135 | ```js
|
133 | 136 | class StopWatchElement extends HTMLElement {
|
@@ -172,7 +175,7 @@ class StopWatchElement extends HTMLElement {
|
172 | 175 | }
|
173 | 176 | ```
|
174 | 177 |
|
175 |
| -### Using Signals to clean up `disconnectedCallback()` |
| 178 | +### Using Cancellation Signals to clean up `disconnectedCallback()` |
176 | 179 |
|
177 | 180 | _Web Components_ that use the `connectedCallback()` lifecycle hook to set things up typically want to tear down those
|
178 | 181 | same things in the `disconnectedCallback()`, but this can sometimes get a little unwieldy. Instead of mirroring
|
@@ -214,12 +217,12 @@ class StopWatchElement extends HTMLElement {
|
214 | 217 | }
|
215 | 218 | ```
|
216 | 219 |
|
217 |
| -### Using signals to cancel old requests |
| 220 | +### Using Cancellation Signals to cancel old requests |
218 | 221 |
|
219 | 222 | A common task that components might do is turn a user action into a network fetch. For example a search input might
|
220 | 223 | query the database every time a character is pressed. If the user types into the input fast enough, old network requests
|
221 | 224 | might stay _in-flight_, saturating the network and delaying newer requests from coming in, making the component feel
|
222 |
| -sluggish. A good way to combat this is to cancel stale requests by using signals: |
| 225 | +sluggish. A good way to combat this is to cancel stale requests by using _cancellation signals_: |
223 | 226 |
|
224 | 227 | ```js
|
225 | 228 | class SearchInputElement extends HTMLInputElement {
|
|
0 commit comments