This gist<https://gist.github.com/rbuckton/b69be537e41feec7fea7> contains the
TypeScript declarations for the version of CancellationToken I’ve been using
for a number of small projects. The basic API shape is:
```ts
class CancellationTokenSource {
constructor(linkedTokens?: CancellationToken[]);
token: CancellationToken;
cancel(reason?: any): void;
close(): void;
}
class CancellationToken {
static none: CancellationToken;
canBeCanceled: boolean;
canceled: boolean;
reason: any;
throwIfCanceled(reason?: any): void;
register(callback: (reason?: any) => void): CancellationTokenRegistration;
}
interface CancellationTokenRegistration {
unregister(): void;
}
```
The approach I use has the following considerations:
• The responsibility for cancellation is split between two types:
o CancellationTokenSource – This type is responsible for propagating a
cancellation signal and linking multiple tokens together (useful to cancel a
graph of async operations, such as cancelling all pending fetch request).
o CancellationToken – A read-only sink for a cancellation signal.
Asynchronous operations can either synchronously observe whether the
cancellation signal has been set, or register an callback for asynchronous
notification of a cancellation signal.
• Once a callback has been registered for asynchronous notification of a
cancellation signal, it can later be unregistered.
• Asynchronous notifications are queued and handled at a higher priority
than Promise continuations.
• The reason for cancellation can be explicitly supplied. If not
supplied, the reason would be a generic “The operation was canceled” error.
Splitting a cancellation signal across two classes has several benefits. One
benefit is that it allows the producer of a cancellation signal to limit access
to the ability to initiate that signal. This is important if you wish to have a
single shared cancellation source for multiple concurrent asynchronous
operations, but need to hand off a cancellation token to a third party without
worrying that the third party code would unintentionally cancel operations that
it does not itself own. Another benefit is the ability to create more complex
cancellation graphs through linking multiple cancellation tokens.
I agree with Domenic that it is necessary to be able to synchronously observe
the canceled state of a cancellation token, and that it is also necessary to be
able to observe a cancellation signal asynchronously. However, I have found
that the asynchronous notification needs to be triggered at a higher priority
than Promise “then” tasks. Consider the following, albeit contrived, example:
```js
let source = new CancellationTokenSource();
let p = new Promise((resolve, reject) => {
Promise.resolve(1).then(resolve);
source.token.register(reject);
source.cancel();
});
```
Since “source.cancel()” is executed synchronously, one would expect that `p`
would be rejected. If the notification were merely added to the same micro-task
queue as Promise “then” tasks, `p` would be resolved first. By making the
micro-task queue into a priority queue, we can allow cancellation notifications
to be processed at a higher priority while still keeping the API itself
asynchronous. In .NET, cancellation notifications are executed synchronously,
however this conflicts with the intent to ensure an asynchronous API is
consistently asynchronous.
This approach also allows cancellation tokens to be more general-purpose. By
not directly tying cancellation into Promises, they can be used with other
future asynchronous primitives such as Observables, async generators, etc.
Ron
From: Kevin Smith<mailto:[email protected]>
Sent: Monday, January 4, 2016 11:44 AM
To: Tab Atkins Jr.<mailto:[email protected]>; Domenic
Denicola<mailto:[email protected]>
Cc: es-discuss<mailto:[email protected]>
Subject: Re: Promises as Cancelation Tokens
The best approach in cases like this is to avoid the word altogether.
The fact that there's confusion at all means people will mess it up
and get annoyed, even if there's a "winner" in overall usage.
Hmmm... Maybe
class CancelToken {
constructor(init);
get cancelRequested() : bool;
whenCancelRequested(callback) : void;
}
Or more/overly tersely:
class CancelToken {
constructor(init);
get requested() : bool;
whenRequested(callback) : void;
}
_______________________________________________
es-discuss mailing list
[email protected]
https://mail.mozilla.org/listinfo/es-discuss