Cancellation and Concurrency
You will learn
- How to cancel in-flight operations
- What concurrency strategies are available out-of-the-box
- Limitations of concurrency applied to operations
- How to react on cancelled operations
Operator concurrency
All concurrency settings are controlled by the concurrency
operator. It accepts any Query or Mutation and configuration object which determines concurrency settings.
Cancel all in-flight operations
If you want to cancel all in-flight operations, you can use the abortAll
option. It accepts an Event that should be called to cancel all in-flight operations.
import { createEvent } from 'effector';
import { concurrency, createQuery } from '@farfetched/core';
const query = createQuery({
/* ... */
});
const somethingHappened = createEvent();
// Anytime you call `somethingHappened`,
// all in-flight operations will be cancelled immediately
concurrency(query, { abortAll: somethingHappened });
Concurrency strategies
However, if you want to control concurrency on a per-operation basis, you can use the strategy
option. It accepts one of the following values:
TAKE_EVERY
In this strategy, every time you call the operation, it will be executed immediately, regardless of whether there are any in-flight operations or not. It is a default strategy for all operations.
import { concurrency, createQuery } from '@farfetched/core';
const query = createQuery({
/* ... */
});
concurrency(query, { strategy: 'TAKE_EVERY' });
TAKE_LATEST
By setting this strategy, you ensure that only the latest operation will be executed. If there are any in-flight operations, they will be cancelled immediately.
import { concurrency, createQuery } from '@farfetched/core';
const query = createQuery({
/* ... */
});
concurrency(query, { strategy: 'TAKE_LATEST' });
TAKE_FIRST
In this strategy, only the first operation will be executed. If there are any in-flight operations, the new operation execution will be skipped.
import { concurrency, createQuery } from '@farfetched/core';
const query = createQuery({
/* ... */
});
concurrency(query, { strategy: 'TAKE_FIRST' });
Limitations
onAbort
TIP
If you are using createJsonQuery
and createJsonMutation
, you do not have to worry about the onAbort
option. It is handled internally by the library.
onAbort
is used to connect the operation's cancellation with internal cancellation mechanism on Farfetched. You have to call this function inside the handler of the operation before any asynchronous operation is started.
It works pretty straightforward: when Farfetched cancels the operation, it will call the function you passed to onAbort
. You can use it to cancel any asynchronous operation you have started.
Fetch API example
In case of using the Fetch API, you should use the onAbort
function to bind a AbortController
and Farfetched's concurrency mechanism.
import { onAbort, createQuery } from '@farfetched/core';
const query = createQuery({
async handler() {
const abortController = new AbortController();
onAbort(() => {
abortController.abort();
});
const response = await fetch('https://example.com', {
signal: abortController.signal,
});
return response.text();
},
});
axios
example
The same approach can be used with axios
library.
import axios from 'axios';
import { onAbort, createQuery } from '@farfetched/core';
const query = createQuery({
async handler() {
const abortController = new AbortController();
onAbort(() => {
abortController.abort();
});
return axios.get('https://example.com', {
signal: controller.signal,
});
},
});
Migration in v0.12-0.14
Originally some debatable decisions were made regarding the concurrency
in factories createJsonQuery
and createJsonMutation
. Please, read the ADR to understand the reasons behind the changes and the migration path.
Reactions
Sometimes you need to react on cancelled operations. For example, you may want to show a message to the user that the operation was cancelled. You can use the .aborted
Event of the operation to react on cancellation.
Both Query and Mutation have the .aborted
event.
import { createQuery } from '@farfetched/core';
const query = createQuery({
/* ... */
});
query.aborted // will be called when the operation is cancelled