Re: [whatwg/dom] Expose an `aborted` promise on AbortSignal? (#946)

Sketching a pattern that doesn't rely on GC:

```js
function abortableSetTimeout(ms, { signal } = {}) {
  return abortableTask(signal, (setOnAbort) => new Promise((resolve) => {
    const timerId = setTimeout(() => resolve(), ms);
    setOnAbort(() => clearTimeout(timerId));
  }));
}
```

Where this is the implementation of `abortableTask`:

```js
/**
 * @param {AbortSignal | undefined} signal
 * @param {(setAbort: () => void) => void | undefined} taskCallback
 */
async function abortableTask(signal, taskCallback) {
  if (signal?.aborted) throw new DOMException("AbortError", "AbortError");
  let onAbort, listener;
  const setOnAbort = (callback) => (onAbort = callback);
  const promise = taskCallback(setOnAbort);

  return Promise.race([
    new Promise((_, reject) => {
      listener = () => {
        onAbort?.();
        reject(new DOMException("AbortError", "AbortError"));
      };
      signal?.addEventListener("abort", listener);
    }),
    promise,
  ]).finally(() => signal?.removeEventListener("abort", listener));
}
```

Some benefits of this:

- The task doesn't run at all if the signal has already aborted.
- `setOnAbort` can be called multiple times, which handles cases where the way to abort changes during different phases of the task.
- Works with primitives (`timerId` doesn't need to be an object)

I don't think it leaks, but I'd like a second opinion on that 😄. 

Here's the DB example:

```js
class DB {
  // wraps a database with AbortSignal API support
  query(string, { signal } = {}) {
    return abortableTask(signal, async (setOnAbort) => {
      await this.open();
      const query = this.createQuery(string);
      setOnAbort(() => query.cancel());
      return query.promise();
    })
  }
  // rest of the implementation
}
```

-- 
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-773954201

Received on Friday, 5 February 2021 10:46:09 UTC