- 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