1

This is a normal example to read a file:

var fs = require('fs');
fs.readFile('./gparted-live-0.18.0-2-i486.iso', function (err, data) {
  console.log(data.length);
});
console.log('All done.');

the code above outputs:

All done.
187695104

whereas this is my own version of a callback, I hope it could be async like the file reading code above, but it is not:

var f = function(cb) {
    cb();
};
f(function() {
    var i = 0;
    // Do some very long job.
    while(++i < (1<<30)) {}
    console.log('Cb comes back.')
});
console.log('All done.'); 

the code above outputs:

Cb comes back.
All done.

Up till now, it's clear that in the first version of the file reading code, All done. is always printed before the file is read. However, in the second my home brewed version of code, All done. is always waiting until the very long job is done.

So what on earth is the magic that makes fs.readFile's callback an async call back while mine is not?

3 Answers 3

3
var f = function(cb) {
    cb();
};

Is not async because it invokes cb immediately.

I think you want

var f = function(cb) {
   setImmediate(function(){ cb(); });
};
Sign up to request clarification or add additional context in comments.

1 Comment

The new setImmediate is likely what you actually want (yes it's confusing, but they're slightly different) nodejs.org/api/timers.html
3

In your example the while-loop is occupying the event-loop therefore the function call to console.log('All done.') is queued on the stack. When the event-loop becomes unblocked the subsequent function calls will be called in sequence.

In Mastering Node.js by Sandro Pasquali - Chapter 2, he discusses deferred execution and the event-loop in order to avoid the issue of the event-loop taking hold and blocking execution. I recommend reading that chapter in order to better understand this non-intuitive way of working in Node.js.

From Mastering Node.js...

Node processes JavaScript instructions using a single thread. Within your JavaScript program no two operations will ever execute at exactly the same moment, as might happen in a multithreaded environment. Understanding this fact is essential to understanding how a Node program, or process, is designed and runs.

The use of setImmediate() can remedy this issue.

Comments

2

You can use setImmediate() to defer the execution of code until the next cycle of the event loop, which I think accomplishes what you want:

var f = function(cb) {
  cb();
};

f(function() {
  setImmediate(function() {
    var i = 0;
    // Do some very long job.
    while(++i < (1<<30)) {}
    console.log('Cb comes back.')
  });
});
console.log('All done.');

The documentation for setImmediate explains the difference between process.nextTick and setImmediate thusly:

Immediates are queued in the order created, and are popped off the queue once per loop iteration. This is different from process.nextTick which will execute process.maxTickDepth queued callbacks per iteration. setImmediate will yield to the event loop after firing a queued callback to make sure I/O is not being starved. While order is preserved for execution, other I/O events may fire between any two scheduled immediate callbacks.

Edit: Update answer based on @generalhenry's comment.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.