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:zenpars...@gmail.com>
Sent: Monday, January 4, 2016 11:44 AM
To: Tab Atkins Jr.<mailto:jackalm...@gmail.com>; Domenic 
Denicola<mailto:d...@domenic.me>
Cc: es-discuss<mailto:es-discuss@mozilla.org>
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
es-discuss@mozilla.org
https://mail.mozilla.org/listinfo/es-discuss

Reply via email to