I have this:
this.toggleWaiting()
this.results = await this.query(term)
this.toggleWaiting()
First a loading spinner gets triggered. Then the query runs. And when the query function ended the loading spinner gets closed.
But what if I want to just show the loading spinner, when the query takes maybe more then 0.5 seconds?
Is there a simple way to do this?
A way to achieve this is to pass the this.query(term)
promise to a function which will handle triggering the toggleWaiting
only when the query takes longer than the specified amount of time (using the timeout).
For example, the below takes a promise, a function (waitingFn
) which will be called with the isWaiting
status as well as a timeout
which you can use to specify how long you want to wait before you show the loading spinner. Finally, when the promise has been fulfilled, we return the result:
async function handleWaiting(promise, waitingFn, timeout) {
let loadingStarted = false;
let timeoutInstance = null;
const timeoutPromise = new Promise((res) => {
timeoutInstance = setTimeout(() => {
loadingStarted = true;
waitingFn(true);
}, timeout);
return res();
});
function onFinished() {
clearTimeout(timeoutInstance);
if (loadingStarted) {
waitingFn(false);
}
}
try {
const [result] = await Promise.all([promise, timeoutPromise]);
onFinished();
return result;
} catch (ex) {
onFinished();
throw ex;
}
}
You can call the handleWaiting
function like so:
const result = await handleWaiting(this.query(term), (isWaiting) => this.toggleWaiting(), 500);
As @FZs and @Bergi have pointed out (thank you both), the below is an antipattern due to using the promise constructor:
function handleWaiting(promise, waitingFn, timeout) {
return new Promise((res, rej) => {
let loadingStarted = false;
const timeoutInstance = setTimeout(() => {
loadingStarted = true;
waitingFn(true);
}, timeout);
function onFinished() {
if (loadingStarted) {
waitingFn(false);
}
clearTimeout(timeoutInstance);
}
return promise
.then((result) => {
onFinished();
res(result);
})
.catch((ex) => {
onFinished();
rej(ex);
});
});
}