codependent
codependent

Reputation: 24452

Understanding Node.js event loop. process.nextTick() never invoked. Why?

I am experimenting with the event loop. First I begin with this straightforward code to read and print the contents of a file:

var fs = require('fs');
var PATH = "./.gitignore";

fs.readFile(PATH,"utf-8",function(err,text){
    console.log("----read: "+text);
});

Then I place it into an infinite loop. In this case, the readFile function is never executed. If I am not mistaken it's because Node's single thread is busy iterating without letting I/O calls be executed.

while(true){
    var fs = require('fs');
    var PATH = "./.gitignore";

    fs.readFile(PATH,"utf-8",function(err,text){
        console.log("----read: "+text);
    });
}

So, I would like to do something so that I/O calls are assigned process time intertwined with the loop. I tried with process.nextTick() but it doesn't work:

while(true){
    process.nextTick(function(){
        fs.readFile(PATH,"utf-8",function(err,text){
            console.log("----read: "+text)
        });
    });
}

Why isn't it working and how could I make it?

Upvotes: 10

Views: 5787

Answers (1)

JayKuri
JayKuri

Reputation: 849

Because your while loop is still running. It's just infinitely adding things to do in the next tick. If you let it go, your node process will crash as it runs out of memory.

When you work with async code, your normal loops and control structures tend to trip you up. The reason is that they execute synchronously in one step of the event loop. Until something happens that yields control to the event loop again, nothing 'nextTick' will happen.

Think of it like this, You are in Pass B of the event loop when your code runs. When you call

process.nextTick(function foo() { do.stuff(); })' 

you are adding the foo to the list of 'things to do before you start pass C of the event loop.' Every time you call nextTick, you add one more thing to the list, but none of them will run until the synchronous code is done.

What you need to do instead is create 'do the next thing' links in your callbacks. Think linked-lists.

// var files = your list of files;
function do_read(count) {
     var next = count+1;
     fs.readFile(files[count], "utf-8", function(err,text) {
         console.log("----read: " + text);

         if (next < files.length) {
            // this doesn't run until the previous readFile completes.
            process.nextTick(function() { do_read(next) });
         }
     });
}
// kick off the first one:
do_read(files[0], 0);

(obviously this is a contrived example, but you get the idea)

This causes each 'next file' to be added to the 'nextTick' to-do queue only after the previous one has been fully processed.

TL;DR: Most of the time, you don't want to start it doing the next thing until the previous thing is completed

Hope that helps!

Upvotes: 24

Related Questions