Ordering Async Code For Parallel Execution
Some Async Functions To Run In "Parallel"
Here's an example of some async code.
3 different functions, that take 3 different sets of time to complete.
const LONG_TIME = 500;
const MID_TIME = 250;
const SHORT_TIME = 125;
function longestFn(cb){
setTimeout(() => {
cb(null, `Longest wait done`)
}, LONG_TIME)
}
function midFn(cb){
setTimeout(() => {
cb(null, `mid wait done`)
}, MID_TIME)
}
function shortestFn(cb){
setTimeout(() => {
cb(null, `shortest wait done`)
}, SHORT_TIME)
}
function printResOrErr(err, s){
if (err) {
console.error(err);
} else {
console.log(s);
}
}
- Above, 3 functions, each do almost the exact same thing
- each takes a callback as a param
- each calls the native
setTimeout
fn - each has a different time to wait for the callback to run (hopefully with functions named to match the timeout duration: mid, shortest and longest)
- each logs a string
Run In Parallel And Output In Reverse Order
One interesting detail could be to run them in order longestFn
,midFn
,shortestFn
(with printResOrErr as the callback to see the output) and see how the output is actually in reverse order: shortest, mid, then longest.
longestFn(printResOrErr)
midFn(printResOrErr)
shortestFn(printResOrErr)
that will log shortest wait done
then mid wait done
then longest wait done
.
This is, perhaps, an "introductory" example of how the event loop can be leveraged.
here, the setTimeout
leverages the event loop to "wait" until the timeout duration runs before "running" the function that was passed to the setTimeout function.
These will run in "parallel". "Parallel" here refers to the event-loop's perspective. The Event-Loop "sees" the 3 instances of setTimeout
all running simultaneously. The event-loop is "ready" to do other logic while the 3x setTimeout calls are "waiting" in the background.
This "parallel" detail is a critical aspect of the event-loop and the control flow of this example.
A Look With More Logs
One way to understand the flow control of this type of logic is good-ol' console.logging.
Putting log statements, with numbers, can reveal the "order" that the code is being run.
If you're confused about the order and have not done this type of exercise before, try putting console.log(1)
in the first place that you think "runs", followed by console.log(2)
in the second...until you've guessed at the run order.
Here, maybe a "spoiler alert", is the run order with the logs in order so that they print from lowest to highest. This might be revealing!
const LONG_TIME = 500;
const MID_TIME = 250;
const SHORT_TIME = 125;
console.log('0');
function longestFn(cb) {
console.log('2');
setTimeout(() => {
console.log('9');
cb(null, `Longest wait done`);
}, LONG_TIME);
}
function midFn(cb) {
console.log('4');
setTimeout(() => {
console.log('8');
cb(null, `mid wait done`);
}, MID_TIME);
}
function shortestFn(cb) {
console.log('6');
setTimeout(() => {
console.log('7')
cb(null, `shortest wait done`);
}, SHORT_TIME);
}
function printResOrErr(err, s) {
if (err) {
console.error(err);
} else {
console.log(s);
}
}
console.log('1');
longestFn(printResOrErr);
console.log('3');
midFn(printResOrErr);
console.log('5');
shortestFn(printResOrErr);
Maybe a TL;DR on the order here:
- we won't "See" javascript "storing" the function creation in memory as part of the process running time
-
0
and1
get logged
-
- once js "gets to" the
longestFn()
command, js "enters" that fn2
gets logged
- the
longestFn()
fn contains asetTimeout
which the logic ofsetTImeout
irself is outside of the code written here. JS "stores" the content of thesetTimeout
function as a callback after the time,LONG_TIME
has passed. The js "runner" is not "skipping" the contents of the setTimeout, rather "waiting" for that time to pass before "calling" the contents of it - after the
longestFn()
code is run, including storing some callback info in the setTimout instance, the js "runner" moves on to the next line afterlongestFn()
3
is logged
- once js "gets to" the
midFn()
command, js "enters" that fn4
gets logged
- the
midFn()
fn contains asetTimeout
which the logic ofsetTImeout
irself is outside of the code written here. JS "stores" the content of thesetTimeout
function as a callback after the time,MID_TIME
has passed. The js "runner" is not "skipping" the contents of the setTimeout, rather "waiting" for that time to pass before "calling" the contents of it - after the
midFn()
code is run, including storing some callback info in the setTimout instance, the js "runner" moves on to the next line afterlongestFn()
5
is logged
- once js "gets to" the
shortestFn()
command, js "enters" that fn6
gets logged
- the
shortestFn()
fn contains asetTimeout
which the logic ofsetTImeout
irself is outside of the code written here. JS "stores" the content of thesetTimeout
function as a callback after the time,SHORT_TIME
has passed. The js "runner" is not "skipping" the contents of the setTimeout, rather "waiting" for that time to pass before "calling" the contents of it - the 3x
setTimeout
instances have "started" in the bg and the shortest instance gets completed, and its callback "called"7
is logged
- the middle-length instance of setTimeout gets completed, and its callback "called"
8
is logged
- the longest-length instance of setTimeout gets completed, and its callback "called"
9
is logged