JavaScript Higher Order Functions, await and loops
Passing functions as arguments to other JavaScript functions or creating higher order functions is something you may want to do or even be encouraged to do. JavaScript is notorious for callback hell, but I would like to demonstrate the situation with respect to async function parameter passing.
Synchronous Steps
Take this example as a function to pass in
const checker = (input) => { return input === 4 };
First off let's try to pass it in with an array of inputs
and use it like so
const happyTimes = (inputs, stopFn) => {
inputs.forEach(z => {
if (stopFn(z)) {
console.log(`Stop reached ${z}`);
break;
}
console.log(`Current: ${z}`);
});
};
Oops can't define this as a forEach enumerator does not have access to break
VM97:5 Uncaught SyntaxError: Illegal break statement
Ok let's try this then
const happyTimes = (inputs, stopFn) => {
inputs.forEach(z => {
if (stopFn(z)) {
console.log(`Stop reached ${z}`);
return true;
}
console.log(`Current: ${z}`);
});
};
That compiles, so it can be called as so
happyTimes([1, 4, 5], checker)
Current: 1
Stop reached 4
Current: 5
The checker function works, but it didn't stop the looping
Modify the loop to use some
and it does work
const happyTimes = (inputs, stopFn) => {
inputs.some(z => {
if (stopFn(z)) {
console.log(`Stop reached ${z}`);
return true;
}
console.log(`Current: ${z}`);
});
};
Yes it works (but this is all synchronous function calls)
happyTimes([1, 4, 5], checker)
Current: 1
Stop reached 4
Asynchronous Steps
Start with changing the checker function to asynchronous
const checker = async (input) => { return await input === 4 };
Make the caller function async as well
const happyTimes = async (inputs, stopFn) => {
inputs.some(z => {
if (await (stopFn(z))) {
console.log(`Stop reached ${z}`);
return true;
}
console.log(`Current: ${z}`);
});
};
VM195:3 Uncaught (in promise)
ReferenceError: await is not defined
at <anonymous>:3:3
at Array.some (<anonymous>)
at happyTimes (<anonymous>:2:9)
at <anonymous>:1:1
Hmm, ok change the looping to item of iterable
instead like so
const happyTimes = async (inputs, stopFn) => {
for (z of inputs) {
if (await (stopFn(z))) {
console.log(`Stop reached ${z}`);
break;
}
console.log(`Current: ${z}`);
}
};
And it works, Yay!
await happyTimes([1, 4, 5], checker)
Current: 1
Stop reached 4
Extra
Change the checker function to throw an exception like so, back as a synchronous function, with the synchronous caller function as well
const checker = (input) => { if(input === 4) { throw new Error("boom"); } };
VM221:1 Uncaught
Error: boom
at checker (<anonymous>:1:54)
at <anonymous>:3:7
at Array.forEach (<anonymous>)
at happyTimes (<anonymous>:2:9)
at <anonymous>:1:1
Allows you to break out of a forEach loop with an exception, seems problematic but is available