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

Top comments (0)

An Animated Guide to Node.js Event Loop

Node.js doesn’t stop from running other operations because of Libuv, a C++ library responsible for the event loop and asynchronously handling tasks such as network requests, DNS resolution, file system operations, data encryption, etc.

What happens under the hood when Node.js works on tasks such as database queries? We will explore it by following this piece of code step by step.