The Event Loop - In Action
Node Parses Some of the File For Errors
The first thing node does with a file is "parse"
How A Few Functions Interact With The Event Loop
Start with a simple node file - lets call it something like "eventloop.js" -
console.log("start")
console.log("end")
Run the file in a terminal:
# run it
node eventloop.js
# output here
start
end
setImmediate to do it later
Add in a setImmediate call:
console.log("start")
setImmediate(() => {
console.log("set immediate")
})
console.log("end")
Run the file in a terminal:
# run it
node eventloop.js
# output here
start
end
set immediate
ALERT!
The end
string logs before the setImmediate string. This is due to node's parsing of the entire "program", here the file. Node parses the entire file before finishing the event-loop logic of setImmediate
.
If you're reading this and "new" to event loop logic, this is officially the end of the beginning.
Compare setImmediate to setTimeout
Throw in a setTimeout:
console.log("start")
setImmediate(() => {
console.log("set immediate")
})
setTimeout(() => {
console.log("timeout")
}, 0)
console.log("end")
Run it:
# run it
node eventloop.js
# output here
start
end
set immediate
timeout
Let's put the timeout + immediate in vice-versa order:
console.log("start")
setTimeout(() => {
console.log("timeout")
}, 0)
setImmediate(() => {
console.log("set immediate")
})
console.log("end")
Run it:
start
end
set immediate
timeout
Maybe not that interesting so far.
Introduce Multiple timeouts for further inspection
console.log("start")
// round 1
setImmediate(() => {
console.log("immediate")
})
setTimeout(() => {
console.log("timeout")
}, 0)
// round 2
setImmediate(() => {
console.log("immediate 2")
})
setTimeout(() => {
console.log("timeout 2")
}, 0)
console.log("end")
Run it:
# run it
node eventloop.js
# output
start
end
immediate
immediate 2
timeout
timeout 2
Here! Node processed BOTH setImmediates before the timeouts! Interesting.
Flip the order of the code - put the timeouts before the immediates && lets see what happens.
Code:
console.log("start")
// timeouts
setTimeout(() => {
console.log("timeout")
}, 0)
setTimeout(() => {
console.log("timeout 2")
}, 0)
// immediates
setImmediate(() => {
console.log("immediate")
})
setImmediate(() => {
console.log("immediate 2")
})
console.log("end")
returns
start
end
timeout
timeout 2
immediate
immediate 2
Interesting! Node processed the timeouts first here.
Next, put the immediates first:
console.log("start")
// immediates
setImmediate(() => {
console.log("immediate")
})
setImmediate(() => {
console.log("immediate 2")
})
// timeouts
setTimeout(() => {
console.log("timeout")
}, 0)
setTimeout(() => {
console.log("timeout 2")
}, 0)
console.log("end")
This returns
start
end
timeout
timeout 2
immediate
immediate 2
This is also interesting! A take-away:
(note: A complete collection of the take-aways is at the end of this doc)
- managing timeouts + immediates can be complicated: managing the priority and order of those two seem interchangable (at least at this trivial scale)
Including an I/O call
Here, a moderately more complex event loop + setImmediate example:
- parse the file (start + end logs)
- setImmediate
- read a file + pass a callback (do that later)
- in the file-handling callback, call setTimeout && setImmediate
console.log("start")
const fs = require("fs")
console.log(`fs has ${Object.keys(fs).length} keys`)
fs.readFile(__filename, () => {
console.log("readFile")
setTimeout(() => {
console.log("readFile: timeout")
}, 0)
setImmediate(() => {
console.log("readFile: immediate")
})
})
setImmediate(() => {
console.log("set immediate")
})
console.log("end")
This will return
start
fs has 101 keys
end
set immediate
readFile
readFile: immediate
readFile: timeout
A few take-aways to consider here:
- node "parses" the whole file(s) before "running" the files
- node will complete the "require"(s) functionality before parsing the rest of the file
- in the I/O callback, node ran setImmediate before setTimeout, even with timeout value of 0 - looks like node FORCES immediates to run before timeouts SPECIFICALLY IN I/O Callbacks
Some Take-Aways
- node "parses" the whole file(s) before "running" the files
- node will complete the "require"(s) functionality before parsing the rest of the file
- in the I/O callback, node ran setImmediate before setTimeout, even with timeout value of 0 - looks like node FORCES immediates to run before timeouts SPECIFICALLY IN I/O Callbacks