I have a simple setTimeout function in Javascript that just allows me to specify the amount of time to delay whatever action by and then just a callback to use for chaining
function delay(item, callback) {
return new Promise(function(response, reject) {
setTimeout(function() {
console.log(item.message);
response(callback());
}, item.time);
});
}
I'm able to use it fine with nesting callbacks but it starts to become very tedious and ugly to use for longer chains
function delayChain() {
const items = [
{message:"Waited 01 sec", time:1000},
{message:"Waited 02 sec", time:2000},
{message:"Waited 04 sec", time:4000},
{message:"Waited 03 sec", time:3000}
];
delay(items[0], function() {
delay(items[1], function() {
delay(items[2], function() {
delay(items[3], function() {
console.log("Done Waiting");
});
});
});
});
}
I was wondering if it is possible to do something similar but in a recursive way
UPDATE
It seems that something similar can be done without the need of callbacks by using async/await like this
async function delayChainAsync() {
const items = [
{message:"Waited 01 sec", time:1000},
{message:"Waited 02 sec", time:2000},
{message:"Waited 04 sec", time:4000},
{message:"Waited 03 sec", time:3000}
];
for(let item of items) {
await delay(item, function() {});
}
console.log("Done Waiting");
}
But I'm hoping to still make use of the callback chaining like in the original delay function
CodePudding user response:
One approach is to create the nested functions using a loop on items
. The idea is to loop backward through items
, creating the most inner nested function first, and keeping a reference to it so that when you move to the next item down your array, you can create the next nested function that uses the previous one you created as the callback to delay
. Once the loop is finished, you can call the final function you created to initiate the outer most function to begin the callback chain:
function delay(item, callback) {
setTimeout(function() {
console.log(item.message);
callback();
}, item.time);
}
function delayChain() {
const items = [ {message:"Waited 01 sec", time:1000}, {message:"Waited 02 sec", time:2000}, {message:"Waited 04 sec", time:4000}, {message:"Waited 03 sec", time:3000} ];
let nextFn = () => console.log("Done waiting"); // the most inner nested function
for(let i = items.length - 1; i >= 0; i--) {
const ref = nextFn; // required so that the closure below refers to this current function and doesn't change as we update `nextFn` through our loop iterations
nextFn = function() {
delay(items[i], ref);
};
}
nextFn();
}
delayChain();
Another option is to do this "pseudo recursively" by passing a counter/index to delayChain
to determine which object we need to create the delay
for in each new call to delayChain
:
function delay(item, callback) {
setTimeout(function() {
console.log(item.message);
callback();
}, item.time);
}
function delayChain(i = 0) {
const items = [ {message:"Waited 01 sec", time:1000}, {message:"Waited 02 sec", time:2000}, {message:"Waited 04 sec", time:4000}, {message:"Waited 03 sec", time:3000} ];
if(i === items.length)
console.log("Done waiting"); // the most inner nested function
else
delay(items[i], () => delayChain( i)); // recursively call `delayChain`
}
delayChain();
Also, note that your delay
function does not need to return a Promise. As you're trying to work with callbacks, there is no need for it to return a Promise
as that Promise
value is never used. Most of the time, your function should either accept a callback as an argument or return a promise, but not both.