Read more

Canceling promises

Henning Koch
December 29, 2018Software engineer at makandra GmbH

The cancelable promises proposal Show archive.org snapshot was withdrawn some time ago.

Illustration online protection

Rails Long Term Support

Rails LTS provides security patches for old versions of Ruby on Rails (2.3, 3.2, 4.2 and 5.2)

  • Prevents you from data breaches and liability risks
  • Upgrade at your own pace
  • Works with modern Rubies
Read more Show archive.org snapshot

The new standard way is that your long-running function take a AbortSignal Show archive.org snapshot { signal } property. The caller can use this signal to send an abort request to your function. Upon receiving the request, your function should reject its promise with an error.

Async browser functions like fetch() reject their promises with a new DOMException('Message here', 'AbortError') when canceled.

This already has good browser support and can be polyfilled on older browsers.

Example

Here is an async function countDown(). It returns a promise that will be fulfilled after the given number of seconds:

function countDown(seconds) {
  return new Promise((resolve, reject) => {
    setTimeout(resolve, seconds * 1000)  
  })
}

Here is how you would use it:

countDown(10).then(() => console.log("10 seconds have past"))

// prints "10 seconds have past" after 10 seconds

Making countDown() abortable

Here is a variant of countDown() that may be canceled by the caller before the time has elapsed:

function countDown(seconds, options = {}) {
  return new Promise((resolve, reject) => {
    if (options.signal) {
      options.signal.addEventListener('abort', (event) => {
        reject(new DOMException('User aborted countdown', 'AbortError'))
      })
    }
  
    setTimeout(resolve, seconds * 1000)  
  })
}

You use it by passing an AbortSignal as a { signal } option:

let controller = new AbortController()

countDown(10, { signal: controller.signal }).then(
  () => console.log("10 seconds have past"),
  (reason) => console.log("Countdown failed:", reason)
)

controller.abort()

/* prints "Coundown failed: User aborted countdown" in the next microtask */

Compatibility

AbortController is supported by all modern browsers, but not IE11.

If you need IE11 support you can polyfill AbortController with very little code:

window.AbortController ||= class {
  constructor() {
    this.signal = document.createElement('abort-signal')
  }

  abort() {
    this.signal.dispatchEvent(new CustomEvent('abort'))
  }
}
Henning Koch
December 29, 2018Software engineer at makandra GmbH
Posted by Henning Koch to makandra dev (2018-12-29 06:01)