Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Node.js setImmediate executed before I/O callbacks (Event Loop)

Take a look at the following code:

var fs = require('fs');
var pos = 0;

fs.stat(__filename, function() {
 console.log(++pos + " FIRST STAT");
});

fs.stat(__filename, function() {
 console.log(++pos + " LAST STAT");
});

setImmediate(function() {
 console.log(++pos + " IMMEDIATE")
})

As I execute this code, the following result is displayed:

enter image description here

As Node.js documentation explains, the setImmediate is executed after I/O callbacks, but in this example setImmediate is being executed before I/O callbacks, am I missing something?

like image 356
Bruno Joaquim Avatar asked Jun 13 '17 14:06

Bruno Joaquim


2 Answers

The result you are expecting would require the fs.stat methods to return instantaneously - basically before the current event loop ends. But your disk will need, let's say, 2ms to complete the I/O operation. Enough time to run through quite a few event loops. What happens (most likely now) is:

Loop 0 starts
fs.stat called
fs.stat called
immediate callback queued
Loop 0 ends
Loop 1 starts
immediate callback gets called
Loop 1 ends
Loop 2 starts
fs.stat completes, I/O callback queued
fs.stat completes, I/O callback queued
Loop 2 ends
Loop 3 starts
fs.stat I/O callback gets called
fs.stat I/O callback gets called

Actually no one even gurantees that fs.stat 1 completes before fs.stat2. So the result you have posted could also be

1 IMMEDIATE
2 LAST STAT
3 FIRST STAT

What you can do:

  1. You can use the sync versions of fs.stat. But although this is comfortable to use it will reduce throughput of your script as you will block the exeuction of your script for the time fs.stat runs. So if you have to run this part of code frequently use one of the following approaches!
  2. You might want to check out "await" to run the code async but in sequence
  3. If the order of completion does not matter to you you could also simply use Promises to wrap both fs.stat calls. And then just do a Promise.all. Or you could use async (a library) that provides a lot of features for dealing with asynchronicity
  4. ...
like image 148
newBee Avatar answered Nov 03 '22 02:11

newBee


setImmediate does NOT wait for all IO operations to finish. It merely gives priority to IO callbacks. At the time that you call setImmediate here the fs.stat calls simply aren't finished yet, and as such their callbacks aren't scheduled for the event loop yet.

like image 36
Tiddo Avatar answered Nov 03 '22 01:11

Tiddo