We saw already an example of cancellation in the Non blocking calls section. In this section we'll review cancellation in more detail.
Once a task is forked, you can abort its execution using
To see how it works, let's consider a basic example: A background sync which can be started/stopped by some UI commands. Upon receiving a
START_BACKGROUND_SYNC action, we fork a background task that will periodically sync some data from a remote server.
The task will execute continually until a
STOP_BACKGROUND_SYNC action is triggered. Then we cancel the background task and wait again for the next
In the above example, cancellation of
bgSyncTask will use Generator.prototype.return to make the Generator jump directly to the finally block. Here you can use
yield cancelled() to check if the Generator has been cancelled or not.
Cancelling a running task will also cancel the current Effect where the task is blocked at the moment of cancellation.
For example, suppose that at a certain point in an application's lifetime, we have this pending call chain:
yield cancel(task) triggers a cancellation on
subtask, which in turn triggers a cancellation on
So we saw that Cancellation propagates downward (in contrast returned values and uncaught errors propagates upward). You can see it as a contract between the caller (which invokes the async operation) and the callee (the invoked operation). The callee is responsible for performing the operation. If it has completed (either success or error) the outcome propagates up to its caller and eventually to the caller of the caller and so on. That is, callees are responsible for completing the flow.
Now if the callee is still pending and the caller decides to cancel the operation, it triggers a kind of a signal that propagates down to the callee (and possibly to any deep operations called by the callee itself). All deeply pending operations will be cancelled.
There is another direction where the cancellation propagates to as well: the joiners of a task (those blocked on a
yield join(task)) will also be cancelled if the joined task is cancelled. Similarly, any potential callers of those joiners will be cancelled as well (because they are blocked on an operation that has been cancelled from outside).
fork is called it starts the task in the background and also returns task object like we have learned previously. When testing this we have to use utility function
createMockTask. Object returned from this function should be passed to next
next call after fork test. Mock task can then be passed to
cancel for example. Here is test for
main generator which is on top of this page.
You can also use mock task's functions
setError to set mock task's state. For example
It's important to remember that
yield cancel(task) doesn't wait for the cancelled task to finish (i.e. to perform its finally block). The cancel effect behaves like fork. It returns as soon as the cancel was initiated. Once cancelled, a task should normally return as soon as it finishes its cleanup logic.
Besides manual cancellation there are cases where cancellation is triggered automatically
raceeffect. All race competitors, except the winner, are automatically cancelled.
In a parallel effect (
yield all([...])). The parallel effect is rejected as soon as one of the sub-effects is rejected (as implied by
Promise.all). In this case, all the other sub-effects are automatically cancelled.