- From: Andrea Giammarchi <andrea.giammarchi@gmail.com>
- Date: Mon, 2 Mar 2015 21:18:57 +0000
- To: Ron Buckton <rbuckton@chronicles.org>
- Cc: Dean Tribble <tribble@e-dean.com>, Kevin Smith <zenparsing@gmail.com>, "public-script-coord@w3.org" <public-script-coord@w3.org>, es-discuss <es-discuss@mozilla.org>
- Message-ID: <CADA77mgemV2iqaGjgGUAF9RGyR7sOXo545RgCrZhZP1ncV2cmw@mail.gmail.com>
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@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@mozilla.org> on behalf of Dean > Tribble <tribble@e-dean.com> > *Sent:* Monday, March 02, 2015 1:25 PM > *To:* Kevin Smith > *Cc:* public-script-coord@w3.org; es-discuss > *Subject:* Re: Cancellation architectural observations > > On Mon, Mar 2, 2015 at 6:32 AM, Gray Zhang <otakustay@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@mozilla.org > https://mail.mozilla.org/listinfo/es-discuss > >
Received on Monday, 2 March 2015 21:19:24 UTC