I found a few examples on the web that let Promises run with a timeout:
- https://italonascimento.github.io/applying-a-timeout-to-your-promises/
- https://advancedweb.hu/how-to-add-timeout-to-a-promise-in-javascript/
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
}
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' } })
Top comments (0)