This article was originally posted on Medium. 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:
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
.
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:
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.
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:
Nonetheless, let’s apply the promise racing idea to create the fulfillWithTimeLimit
function.
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:
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.
Top comments (2)
That is one really interesting use of
Promise.race
! Thank you for this articleI appreciate it!