Use Promise.race for user-friendly timeouts
Imagine having a long-running process that usually runs within a second, but sometimes it takes longer. A waiting spinner is a user-friendly way of handling this, but if the process is fast it's better not to show it.
How to make sure that only for the long-running processes will the spinner be shown?
Let's say you have a code like this:
const process = async () => {
// get the results
// show the results
}
Use Promise.race
Promise.race
gets an array of Promises and resolves (or rejects) with the first result. Along with the process()
call, let's have a timeout Promise also. Only if the latter finishes first we show the spinner.
First, let's make sure that the processing one never rejects and the timeout never resolves:
[
process().catch(() => undefined),
wait(1000).then(() => Promise.reject())
]
The .catch(() => undefined)
makes sure no matter what the result of the process()
call is, the Promise will be resolved and never rejected. Similarly, the .then(() => Promise.reject())
converts a resolved Promise to a rejected one.
Then we need a Promise.race
and a catch
on the result. A call to the catch
means the timeout is hit:
Promise.race([
process().catch(() => undefined),
wait(1000).then(() => Promise.reject())
])
.catch(() => {
// show the spinner
})
This makes sure the spinner is only shown if the processing goes longer than a second.