you-are-cancelled
v2.0.1
Published
An implementation of the cooperative cancellation model that is heavily inspired by the .NET implementation.
Downloads
25
Maintainers
Readme
you-are-cancelled
An implementation of the cooperative cancellation model that is heavily inspired by the .NET implementation.
Usage
The TL;DR is that there are Cancellation Token Sources where each have a Cancellation Token associated with them. An operation that can be cancelled and spawns one or more async sub-operations would create a Cancellation Token Source and give said sub-operations the resulting Cancellation Token. When interrupted, the operation will request a cancellation using the aforementioned Cancellation Token Source, the associated token will then communicate that cancellation request to all sub-operations allowing them to gracefully abort.
This module can be treated as an ES module:
import { CancellationTokenSource } from 'you-are-cancelled';
This module can also be treated as a CommonJS module:
const { CancellationTokenSource } = require('you-are-cancelled');
Cancellation Token Sources
An operation that can be cancelled and spawns one or more asynchronous sub-operations would create the Cancellation Token Source:
const source = new CancellationTokenSource();
When the operation needs to be cancelled, you can request a cancellation:
setTimeout(function ()
{
source.cancel('Cancelling operation because it took longer than 5 seconds.');
}, 5000);
Cancellation Tokens
The cancellation token is responsible for propagating the cancellation requested by its associative Cancellation Token Source. Every Cancellation Token Source has a unique token associated with it. You will give this token to any asynchronous operation that can be cancelled:
await getRecordsFromDatabase(source.token);
Important: Do not pass around the Cancellation Token Source!
Reacting to cancellation requests
You can register a callback that will execute when a cancellation has been requested:
token.register(function (error)
{
// Clean up...
});
Important: If a cancellation has already been requested, the callback will be executed immediately.
It is important that you deregister the callback when the operation completes. Otherwise you may have a potential memory leak, or face unexpected errors because a cancellation request will still execute your callback. You can achieve this easily like this:
const callback = token.register(function ()
{
// Clean up...
});
try
{
// Perform operation.
}
catch (error)
{
// Handle errors.
}
finally
{
token.deregister(callback);
}
Handling cancellation requests
The cancellation token is also a thenable
object, so you can race it against other promises:
try
{
await Promise.race([operation, token]);
}
catch (error)
{
if (error instanceof OperationCancellationError)
{
// Was cancelled. Could do some clean up here.
}
else
{
// Operation error.
}
}
The cancellation token will never resolve; it will only ever be pending or rejected. When a cancellation is requested, the token is rejected with an OperationCancellationError
with a message that matches the reason provided when said cancellation was requested.
In addition, you can throw an OperationCancellationError
if a cancellation has already been requested:
token.throwIfCancellationRequested();
The Dummy Cancellation Token
When defining an operation that can be cancelled, it is good practice to make the cancellation token optional. The CancellationToken.None
dummy token can help you with this - you can use it as a default parameter value so your code can always assume it has a cancellation token to work with:
async function getRecordsFromDatabase(filter, token = CancellationToken.None)
{
// ...
}
The token state
Each cancellation token has the following state associated with it:
| Flag | Description |
| ------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| isCancelable
| Indicates whether the token is capable of being in a cancelled state. Normally this will be true
, but for the CancellationToken.None
dummy token this will always be false
. |
| isCancellationRequested
| Indicates whether a cancellation has been requested. For the CancellationToken.None
dummy token this will always be false
. |
Abort Signals
This module was developed before the Abort Controller Interface was introduced. To make the migration simpler, or if you simply prefer using this module, a CancellationToken
can be converted to an AbortSignal
:
const signal = token.toAbortSignal();
Important: This will return null
in a runtime environment where AbortController
is not yet supported; it is your responsibility to polyfill that interface if you need to.
Getting started
This module is available through the Node Package Manager (NPM):
npm install you-are-cancelled
Please Note: Versions of Node lower than v12.0.0 are not supported.
Development
Building
You can build UMD and ESM versions of this module that are minified:
npm run build
Testing
This module also has a robust test suite:
npm test
This includes a code quality check using ESLint. Please refer to the .eslintrc
files to familiar yourself with the rules.
License
This project is released under the MIT license.