I wanted to write a function that allowed me to wait for a specific time to run the code. The quick and dirty way to do it was to use setTimeout and nest callbacks. For example, at the end of an action, I would like to play an animation before I remove the element from the page. Let's see how we can do that with setTimeout.
function victoryDance(element) {
// start the animation
element.classList.add("animate");
// after 2 seconds start the clean up
setTimeout(() => {
// stop the animation
element.classList.remove("animate");
// fade after 300 ms
setTimeout(() => {
// fade the element out
element.classList.add("fade-out");
// after 300 ms, remove the element
setTimeout(() => {
element.parentNode.removeChild(element);
}, 300);
}, 300);
}, 2000);
}
The code is self-explanatory. victoryDance takes an element, animates it, stops the animation, fades the element out, then removes it from the DOM. As you can see, each action is nested into a setTimeout. The more actions I want to perform, the deeper I'd have to nest my functions. Instead, here is how I would like my code to look like instead.
function victoryDance(element) {
// start the animation
element.classList.add("animate");
Util.wait(2)
.then( wait => {
element.classList.remove("animate");
return wait(.3);
})
.then( wait => {
element.classList.add("fade-out");
return wait(.3);
})
.then( wait => {
element.parentNode.removeChild(element);
});
}
This is much cleaner, and it is easier to read. If I want to add more delays, I can return wait at the end of each callback and add how much time I need to wait before the next action. To use the then() function, all we have to do is create a Promise. Here is the code.
// Nesting it inside util for portability
const Util = (() => {
const wait = (delay) => {
return new Promise((resolve) => {
setTimeout(() => {
resolve(wait);
}, delay * 1000);
});
};
return {
wait,
};
})();
This is a simple function. wait() returns a Promise. Now when the timer times out, your function is called with a new wait promise as an argument that you can use to delay the next function call when it is resolved. Here is a generic way we can call it.
Util.wait(1)
.then(wait => {
alert(1);
return wait(2);
})
.then(wait => {
alert(2);
return wait(3);
})
.then(wait => {
alert(3);
return wait(4);
})
.then(wait => {
alert(4);
});
When this is executed, alert is called after 1 second, then waits 2 seconds it is called again, then waits 3 seconds and it is called again, etc. This allows you to daisy chain timed actions one after another.
Join the Conversation