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