Cancellation architectural observations

Andrea Giammarchi andrea.giammarchi at gmail.com
Mon Mar 2 13:18:57 PST 2015


So this is my simplified view of the matter ... it already works, and it
aborts eventually with the ability to ignore the onabort callback.

The config object can have `onabort` that activates the "abort-ability",
the `onprogress` so that eventually this promise inside a generator can
still update UIs, and potentially any other sort of property but for demo
sake just `method` for GET, HEAD, PUT, POST and other requests.

```js
function fetch(url, config) {
  config || (config = {});
  var
    xhr = new XMLHttpRequest,
    promise = new Promise(function (res, rej) {
      xhr.addEventListener('error', function (pe) { rej(xhr); });
      xhr.addEventListener('load', function (pe) { res(xhr); });
      if (config.onabort)
        xhr.addEventListener('abort', config.onabort);
      if (config.onprogress)
        xhr.addEventListener('progress', config.onprogress);
      xhr.open(config.method || 'GET', url, true);
      xhr.send(null);
    })
  ;
  if (config.onabort)
    promise.abort = xhr.abort.bind(xhr);
  return promise;
}
```

abort example:
`fetch('?page', {onabort: console.warn.bind(console)}).abort();`

with progress too
`fetch('?page', {onabort: console.warn.bind(console), onprogress:
console.log.bind(console)}).abort();`

full request
`fetch('?page', {onabort: console.warn.bind(console), onprogress:
console.log.bind(console)}).then(console.log.bind(console));`

Why this code? Simply to somehow show that I am all for getting this right,
but to me it's also probably a simpler matter than it looks like, specially
for cases where cancel or abort is meant and needed.

Best Regards


On Mon, Mar 2, 2015 at 7:45 PM, Ron Buckton <rbuckton at chronicles.org> wrote:

>>
> In light of *Async Functions* in ES7, it may make sense to separate the
> abstractions between promises and cancellation. Promises and cancellation
> signals have different use cases:
>
>
>  *Promises*
>
>    - Promises are *consumed* by the caller and *produced* by the callee.
>    - Observation a promise resolution can only happen in a *later * turn.
>    - The consumer of a promise *cannot* directly resolve the promise.
>
> *Cancellation*
>
>    - Cancellation signals are *produced* by the caller and * consumed* by
>    the callee.
>    - Observation a cancellation signal must happen *immediately*.
>    - The consumer of a cancellation token *cannot* directly cancel the
>    token.
>
> *API Proposal:*
>
>
>  class CancellationTokenSource {
>   /** Create a new CTS, optionally with an iterable of linked cancellation
> tokens. */
>   constructor(linkedTokens?: Iterable<CancellationToken>);
>
>   /** Gets the cancellation token for this source. */
>   get token(): CancellationToken;
>
>   /** Cancels the source and sends a cancellation signal with an optional
> reason (default Error("Operation canceled")). */
>   cancel(reason?: any): void;
>
>   /** Cancels the source after a delay (in milliseconds), with an optional
> reason. */
>   cancelAfter(delay: number, reason?: any): void;
>
>   /** Prevents any possible future cancellation of the source and removes
> all linked registrations. */
>   close(): void;
> }
>
> class CancellationToken {
>   /** Gets a cancellation token that can never be canceled. */
>   static get default(): CancellationToken;
>   /** Gets a value indicating whether the cancellation signal was sent. */
>   get canceled(): boolean;
>   /** If canceled, gets the reason for cancellation if provided;
> otherwise, returns `undefined`. */
>   get reason(): any;
>   /** If canceled, throws either the reason or a general “Operation
> Canceled” error. */
>   throwIfCanceled(): void;
>   /**
>     * Registers a callback to execute immediately when a cancellation
> signal is received.
>     * The callback can be removed using the `unregister` method of the
> return value.
>     */
>   register(callback: (reason: any) => void): { unregister(): void };
> }
>
>
>  *Usage (Abort):*
>
>
>  ```
> // aborts and throws error when canceled
> function fetchAsync(url, cancellationToken = CancellationToken.default) {
>   return new Promise((resolve, reject) => {
>     cancellationToken.throwIfCanceled();
>     var xhr = new XMLHttpRequest();
>     var registration = cancellationToken.register(() => xhr.abort());
>     xhr.open("GET", url, /*async*/ true);
>     xhr.onload = event => {
>         registration.unregister();
>         resolve(xhr.responseText);
>     };
>     xhr.onerror = event => {
>         registration.unregister();
>         reject(xhr.statusText);
>     }
>     xhr.send(null);
>   });
> }
>
> fetchAsync(...).then(...); // as expected
>
> var cts1 = new CancellationTokenSource();
> fetchAsync(..., cts1.token).catch(...);
> cts1.cancel(new Error("Operation Canceled"); // .catch gets the error and
> the xhr is aborted.
>
>
>  *Usage (Ignore):*
>
>
>  // ignore operation/stop processing
> async function startTicker(receiveSymbol, cancellationToken =
> CancellationToken.default) {
>     while (!cancellationToken.canceled) {
>         var symbols = await fetchSymbols();
>         for (var symbol of symbols) {
>             receiveSymbol(symbol);
>         }
>     }
> }
>
> var stopTicker = new CancellationTokenSource();
> stopTicker.cancelAfter(5 * 60 * 1000); // stop after 5 minutes.
> startTicker(..., stopTicker.token).catch(...); // .catch only gets error
> from `fetchSymbols`.
>
>
>  Ron
>  ------------------------------
> *From:* es-discuss <es-discuss-bounces at mozilla.org> on behalf of Dean
> Tribble <tribble at e-dean.com>
> *Sent:* Monday, March 02, 2015 1:25 PM
> *To:* Kevin Smith
> *Cc:* public-script-coord at w3.org; es-discuss
> *Subject:* Re: Cancellation architectural observations
>
>   On Mon, Mar 2, 2015 at 6:32 AM, Gray Zhang <otakustay at icloud.com> wrote:
>
>>  +1 to the ignore term, I’ve opened an issue about it in
>> https://github.com/promises-aplus/cancellation-spec/issues/14
>>
> I have little attachment to any term, but there's value in keeping
> terminology that has years of investment and use in other contexts. However
> "ignore" also has the wrong sense, because it implies that the computation
> completes anyway. That can be accomplished more easily by simply dropping
> the promise.
>
>>  IMO the term cancel(or abort) and ignore are totally different things,
>> the former one means “do not continue, stop it right now” and the “stop”
>> state should be broadcast to everyone who is interested in the work, while
>> the latter means “I don’t care about the result anymore, just play it as
>> you like”, it means the async progress can be continued
>>
>  This goes back to some of the observations above: you cannot stop it
> "right now" because async notification is not synchronous; indeed the
> operation may already be complete before you stop it. Thus consumers of the
> result of a cancellable request need to be able to handle either successful
> completion or the cancelled state (which just looks like any other error
> that prevented completion).  Attempting broadcast to "everyone" adds
> complexity and resources that are needed only in the rare cancellation
> case. It's typically not only not worth the software complexity, but not a
> good idea. When you cancel a print job, the document editor should make
> best efforts in the background to stop requesting fonts, stop laying out
> print pages, stop spitting out pages on the printer, etc. but most
> importantly, it should start paying attention to my new edits and hang
> waiting for everything that might be involved in printing to wrap itself up.
>
>>  In practice both scenario are commonly seen, we may abort a resource
>> fetch in order to save bandwidth and opened connections, or we may in other
>> side just ignore it since continue to complete the fetch can result in a
>> local cache, which speeds up our fetch next time
>>
> The resource point is important. That's the "don't care" scenario, not the
> "abort" scenario. It's the request processor that knows what cleanup is
> worth the effort. The initiator of the request only knows they don't care
> about the result anymore.
>
> _______________________________________________
> es-discuss mailing list
> es-discuss at mozilla.org
> https://mail.mozilla.org/listinfo/es-discuss
>
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.mozilla.org/pipermail/es-discuss/attachments/20150302/63b700ba/attachment-0001.html>


More information about the es-discuss mailing list