Set a Time Limit on Async Actions
Tags: javascript, webdev, programming, node
Published: 2021-04-19 17:01:56

_This article was originally posted on [Medium](https://medium.com/swlh/boost-server-performance-with-client-side-image-compression-cdefba1c1c0d). If you prefer reading it from there, please do check it out._

## Introduction

Asynchronous programming is incredible! 

With it, we can run our IO-intensive tasks without having to block the execution of other code. 

However, in situations where the code after a blocking task depends on the task’s result, we must wait. Now, imagine if the blocking task took a long time to finish or it never finished. This could be problematic in the context of the application. 

We can solve this issue by setting a time limit on our task. If our task doesn’t finish in the span of the time limit, we can return a failure value instead. Let me elaborate.

## Concept

Let’s suppose that the blocking task is a promise called `longTask`, and it was used in the following function:

```javascript
    async function doSomething(){
       let data = await longTask;
       doSomethingImportantWithData(data);
    }
```

If the time it takes `longTask` to settle is longer than our requirements or if `longTask` is never settled, we won’t be able to execute the code after `longTask` in a timely manner.

However, imagine if we could set a time limit on our blocking tasks. In the case that the blocking task doesn’t settle within the time limit, we can return a failure value from the task. In the scenario the task resolves, we can return the value it resolved to.

To elaborate, suppose there was a function called `fulfillWithTimeLimit` which takes in milliseconds, the time limit, task, the task promise we would like to set a time limit on, and `failureValue`, the value that would be resolved from `fulfillWithTimeLimit` if `task` never completes within the time limit.

In the case that `longTask` is resolved before the time limit, `fulfillWithTimeLimit` returns with the value resolved from `longTask`. 

In the case that `longTask` never finishes within the span of the time limit, the function should immediately return `failureValue`. 

With this approach, we ensure that we never have to wait on `longTask` for more than the specified time limit.

Let’s dig into the approach.

## Code

In order to “set a time limit” on the task, we can create another promise, `timeoutPromise`, which resolves to `failureValue` after the time limit. After that, we can race both our `timeoutPromise` and task with [`Promise.race`](https://developer.mozilla.org/de/docs/Web/JavaScript/Reference/Global_Objects/Promise/race). 

`Promise.race` takes in a list of promises and resolves or rejects to the value of the promise in the list that is settled first.

To provide an example, suppose I had the two following promises:
```javascript
    const a = new Promise((resolve, reject) => {
        setTimeout(() => {
            resolve("A finished before!");
        }, 100);
    });

    const b = new Promise((resolve, reject) => {
        setTimeout(() => {
            resolve("B finished!");
        }, 1000);
    });
```

Suppose I raced these promises and got the value.

```javascript
    const finishString = await Promise.race([a, b]);
```

Since a resolves after 100 milliseconds while `b` resolves after 1000 milliseconds, `a` will be the first promise to resolve. As a result, `finishString` will be equal to “A finished before!”. If you would like to learn more about `Promise.race`, please check out the following:

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

Nonetheless, let’s apply the promise racing idea to create the `fulfillWithTimeLimit` function.

{% gist https://gist.github.com/Ramko9999/c2a0dd5aaf907724034e98f40e0a6f71

%}

To begin, we create our `timeoutPromise` and ensure it resolves with the `failureValue` after the time limit. Then, we race to see whether our task or `timeoutPromise` finishes first. For safety, we can clear the timeout and return `response`, the resolved value of the race.

Here is how `doSomething` looks now:

{% gist https://gist.github.com/Ramko9999/7e7cd20392f30878da83dde939fa03ba

%}

In the above example, I set `failureValue` to null. However, it may be better to set it to a value of the same type as what is resolved from the task. In fact, it may be better to call reject in the `timeoutPromise` than to resolve with a `failureValue`.

**That’s it!** We can easily reuse `fulfillWithTimeLimit` in our application code where we need a time limit.

## Conclusion

In this blog, I aimed to show a solution with `Promise.race` to handle situations where blocking tasks may fail to settle or take too long to settle. Though I did not cover all the functionalities of promises, I hope this article amplifies your curiosity to explore them more.

## Resources

[**Promise**]
(https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise)

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

[**setTimeout**]
(https://www.tutorialspoint.com/what-is-settimeout-method-in-javascript)