Home

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
Tags: