DEV Community

Jan Küster
Jan Küster

Posted on

Create a JavaScript Promise with timeout

I found a few examples on the web that let Promises run with a timeout:

Both were already good ideas but I wanted some more control of their behaviour and therefore I want to share my modified version.

Code

const defaultMessage = 'promise.timedOut'

/**
 * Lets a promise race against a timeout. If the promise settles before the timeout then it will
 * be the one to use (no matter, whether it has been resolved or rejected).
 *
 * Otherwise the timeout promise will be used to either resolve to a message
 * or reject an error, depending on the used options.
 *
 * @param promise {Promise} the promise to race against the timeout
 * @param timeout {number=}  optional number of milliseconds until timeout, defaults to 1000ms / 1 sec
 * @param throwIfTimedOut {boolean=} optional flag to either reject (if true) or resolve (if false), defaults to false
 * @param message {string=} optional message to be resolved/rejected on timeout, defaults to 'promise.timedOut'
 * @return {Promise<Awaited<unknown>>}
 */
export const createTimedPromise = (promise, { timeout = 1000, throwIfTimedOut = false, message, details } = {}) => {
  let timeOut = undefined

  const race = Promise.race([
    promise,
    new Promise((resolve, reject) => {
      timeOut = setTimeout(() => {
        if (throwIfTimedOut) {
          const error = new Error(message || defaultMessage)
          // I often like to attach contextual information to errors.
          // This is up to you, whether to do this or not
          error.details = details
          return reject(error)
        }
        else {
          return resolve(message || defaultMessage)
        }
      }, timeout)
    })
  ])

  // always clear timeout to prevent weird behaviour
  race.finally(() => clearTimeout(timeOut))

  return race
}
Enter fullscreen mode Exit fullscreen mode

Usage

// all default
await createTimedPromise(new Promise((resolve, reject) => { ... }))

// custom timeout
await createTimedPromise(new Promise((resolve, reject) => { ... }), { timeout: 500 })

// reject if timedout
await createTimedPromise(new Promise((resolve, reject) => { ... }), { throwIfTimedOut: true })

// additional context
await createTimedPromise(new Promise((resolve, reject) => { ... }), { message: 'you lost', details: { foo: 'bar' } })
Enter fullscreen mode Exit fullscreen mode

Oldest comments (0)