Promises: run any promise with a timeout
Tags: javascript, typescript, promises, react
Published: 2022-03-18 15:11:44

---
title: Promises: run any promise with a timeout
published: true
description: 
tags: [javascript, typescript, promises, react]
canonical_url: https://abdellahcodes.com/promises-run-any-promise-with-a-timeout
cover_image: https://dev-to-uploads.s3.amazonaws.com/uploads/articles/0ylr2vxclgga2xzoouqu.png
---

A promise has two states: either pending or settled (resolved or rejected). The user has no control over the time it takes from going from the first state to the second. Which makes it harder to bail out on a certain promise when it takes too long in a promise friendly way.

[`Promise.race()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/race) to the rescue.

## How does `Promise.race` work

This method takes an array of promises and - as its name suggests - races them, the first one to be `settled` in either state wins.

example:

```javascript
const resolveAfter = (duration, value) => new Promise((resolve, reject) => setTimeout(() => resolve(value), duration));

let first = resolveAfter(100, 'value from first');
let second = resolveAfter(200, 'value from second');

Promise.race([first, second]).then(console.log);
// logs 'value from first'
```

And it works with errors too as you might expect:

```javascript
const resolveAfter = (duration, value) => new Promise((resolve, reject) => setTimeout(() => resolve(value), duration));
const rejectAfter = (duration, err) => new Promise((resolve, reject) => setTimeout(() => reject(err), duration));

let first = rejectAfter(100, new Error('oops in first'));
let second = resolveAfter(200, 'value from second');

Promise.race([first, second]).then(console.log).catch(console.error);
// logs: 'Error: oops in first'
```

## Leverage `Promise.race` to race promises against the clock

The fisrst ingredient is a promise that resolves after a timeout. We have already seen that in the previous example.

The second is a specific `Error` `class` to be sure it came from the rejected timeout and not the original promise we were awaiting.
We could implement a specific `class` that extends `Error` like this:

```javascript
class TimeoutError extends Error {
    constructor(...args) {
        super(...args);
    }
}

const resolveAfter = (duration, value) => new Promise((resolve, reject) => setTimeout(() => resolve(value), duration));
const rejectAfter = (duration, err) => new Promise((resolve, reject) => setTimeout(() => reject(err), duration));

let first = rejectAfter(100, new TimeoutError('Timeout!'));
let second = resolveAfter(200, 'value from second');

Promise.race([first, second])
    .then(console.log)
    .catch((err) => {
        if (err instanceof TimeoutError) {
            // handleTimeoutError(err)
        } else {
            // handleOtherError(err)
        }
        console.error(err);
    });

// logs: Error: Timeout!
```

You could imagine moving this logic to its own module and abstracting away the timout logic like this:

```javascript
// module: timeout.js

const rejectAfter = (duration, err) => new Promise((resolve, reject) => setTimeout(() => reject(err), duration));

export class TimeoutError extends Error {
    constructor(...args) {
        super(...args);
    }
}
export const withTimeout = (promise, timeout = 0) => {
    return Promise.race([promise, rejectAfter(100, new TimeoutError('Timeout!'))]);
};

// module: user.js

import { withTimeout, TimeoutError } from './timeout';

const resolveAfter = (duration, value) => new Promise((resolve, reject) => setTimeout(() => resolve(value), duration));

withTimeout(resolveAfter(200, 'value from my promise'), 100).then(console.log).catch(console.error);
// logs: Error: Timeout!
withTimeout(resolveAfter(100, 'value from my promise'), 200).then(console.log).catch(console.error);
// logs: value from my promise
```

## Conclusion

I hope you have found this short article helpful. `Promise.race()` doesn't get a lot of love, but we leveraged it
to solve a commnon question amongst promise users.

If you have any remarks or questions, please leave them in the comments. I will be happy to answer each one of them.

And don't forget to follow for more 🤗