Skip to content

Instantly share code, notes, and snippets.

@kraftdorian
Last active October 15, 2023 14:21
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save kraftdorian/1f55b833e7944640673fc27c63155039 to your computer and use it in GitHub Desktop.
Save kraftdorian/1f55b833e7944640673fc27c63155039 to your computer and use it in GitHub Desktop.
Cancelable Promise - experiment
/**
* @param {AbortSignal} signal
* @param {(ev: AbortSignalEventMap['abort']) => void} onCancel
* @returns {() => void}
*/
function createSignalCancelListener(signal, onCancel) {
signal.addEventListener('abort', onCancel);
return () => signal.removeEventListener('abort', onCancel);
}
/**
* @param {AbortSignal} signal
* @returns {[Promise<void>, () => void]}
*/
function listenToSignal(signal) {
/** @type {(() => void) | undefined} */
let unsub;
return [
new Promise((_0, reject) => {
if (signal.aborted) {
return reject(signal.reason);
}
unsub = createSignalCancelListener(signal, (e) => reject(e.target.reason));
}),
unsub
];
}
/**
* @param {AbortSignal} signal
* @param {Promise<unknown>} promise
* @returns {Promise<unknown>}
*/
async function cancelable(signal, promise) {
const [cancelPromise, stopListening] = listenToSignal(signal);
return Promise.race([promise, cancelPromise]).finally(stopListening);
}
@kraftdorian
Copy link
Author

kraftdorian commented Oct 15, 2023

Usage example:

function clearableTimeout(fn, timeout) {
  const timeoutId = setTimeout(() => {
    fn();
    clearTimeout(timeoutId);
  }, timeout);
  return () => clearTimeout(timeoutId);
}

async function main() {
  const ctrl = new AbortController();

  let stopListening;
  const task = new Promise((resolve) => {
    const cancelTimeout = clearableTimeout(() => {
      resolve('data');
    }, 3000);

    if (ctrl.signal.aborted) {
      return cancelTimeout();
    }
    stopListening = createSignalCancelListener(ctrl.signal, cancelTimeout);
  });

  try {
    const data = await cancelable(ctrl.signal, task);
    stopListening();
    console.log('received data:', data);
    return 0;
  } catch (e) {
    console.error('err:', e);
    return 1;
  }
}

main();

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment