- From: Jake Archibald <notifications@github.com>
- Date: Mon, 08 Feb 2021 03:56:57 -0800
- To: whatwg/dom <dom@noreply.github.com>
- Cc: Subscribed <subscribed@noreply.github.com>
- Message-ID: <whatwg/dom/issues/946/775093185@github.com>
Here's how I'd document the proposal, plus an alternative that's mostly based on @Jamesernator's: --- **`abortSignal.doAbortable(callback)`** `doAbortable` simplifies making an API abortable via an `AbortSignal`. ```js const result = await signal.doAbortable(async (setAbortAction) => { // Do async work here. setAbortAction(() => { // Abort the async work here. }); }); ``` `callback` is called immediately unless abort has been signalled, in which case `doAbortable` returns a promise rejected with an `AbortError`. `callback` should return a promise that settles once the async work is complete. `doAbortable` returns a promise that resolves with return value of `callback`, or rejects with an `AbortError` if abort is signalled. If abort is signalled, the _last_ callback passed to `setAbortAction` will be called. `setAbortAction` can be called with `undefined` if there are no longer any meaningful abort steps. Example: An abortable that resolves with `'hello'` after 10 seconds: ```js const controller = new AbortController(); const signal = controller.signal; const promise = signal.doAbortable((setAbortAction) => { return new Promise((resolve) => { const timerId = setTimeout(() => resolve('hello'), 10_000); setAbortAction(() => clearTimeout(timerId)); }); }); ``` `promise` will resolve with `'hello'` after 10 seconds, unless `controller.abort()` is called within those 10 seconds, in which case `promise` rejects with an `AbortError`. <details> <summary>Implementation</summary> ```js AbortSignal.prototype.doAbortable = function (callback) { if (this.aborted) throw new DOMException('', 'AbortError'); let onAbort, listener; const setAbortAction = (c) => { onAbort = c }; const promise = callback(setAbortAction); return Promise.race([ new Promise((_, reject) => { listener = () => { onAbort?.(); reject(new DOMException('', 'AbortError')); }; this.addEventListener('abort', listener); }), promise, ]).finally(() => this.removeEventListener('abort', listener)); }; ``` </details> --- Alternatively, based on @Jamesernator's idea: **`abortSignal.doAbortable(callback)`** `doAbortable` simplifies making an API abortable via an `AbortSignal`. ```js const result = await signal.doAbortable(async (innerSignal) => { // Do async work here. innerSignal.addEventListener('abort', () => { // Abort the async work here. }); }); ``` `callback` is called immediately unless abort has been signalled, in which case `doAbortable` returns a promise rejected with an `AbortError`. `callback` should return a promise that settles once the async work is complete. `doAbortable` returns a promise that resolves with return value of `callback`, or rejects with an `AbortError` if abort is signalled. `innerSignal` follows the outer signal, meaning it signals abort when the outer signal signals abort. However, it's only active for the duration of `callback`, which improves garbage collection. Example: An abortable that resolves with `'hello'` after 10 seconds: ```js const controller = new AbortController(); const signal = controller.signal; const promise = signal.doAbortable((innerSignal) => { return new Promise((resolve) => { const timerId = setTimeout(() => resolve('hello'), 10_000); innerSignal.addEventListener('abort', () => clearTimeout(timerId)); }); }); ``` `promise` will resolve with `'hello'` after 10 seconds, unless `controller.abort()` is called within those 10 seconds, in which case `promise` rejects with an `AbortError`. <details> <summary>Implementation</summary> ```js AbortSignal.prototype.doAbortable = function (callback) { if (this.aborted) throw new DOMException('', 'AbortError'); const controller = new AbortController(); const innerSignal = controller.signal; const promise = callback(innerSignal); let listener; return Promise.race([ new Promise((_, reject) => { listener = () => { controller.abort(); reject(new DOMException('', 'AbortError')); }; this.addEventListener('abort', listener); }), promise, ]).finally(() => this.removeEventListener('abort', listener)); }; ``` </details> -- You are receiving this because you are subscribed to this thread. Reply to this email directly or view it on GitHub: https://github.com/whatwg/dom/issues/946#issuecomment-775093185
Received on Monday, 8 February 2021 11:57:10 UTC