I'm learning node.js from the Node Beginner Book and the subsequent ebook purchase. In the book, Manuel Kiessling explains that a line of blocking code like this one:
fs.readFileSync(blah);
will block the entire node process and ALL requests coming in. This is really bad for a multi-user website!
This is the example, Kiessling uses:
exec("ls -lah", function( error, stdout, stderr ) {
response.writeHead(200, {"Content-Type": "text/plain"});
response.write(stdout);
response.end();
});
This is the code that tricked me. He says the ls -lah could easily be replaced with a more time consuming operation like find / -name "*" or a database lookup. I assumed the expensive and blocking operation would somehow run in the background explicitly because of the async callback.
So I got to testing my theory with this code:
var http = require("http");
var url = require("url");
badSleep = function(ms) {
var now = new Date();
var finishAtDate = now.getTime() + ms;
console.log("CPU burning sleep for " + ms + " milliseconds");
while(new Date() < finishAtDate) {
// do nothing
}
};
asyncWrapper = function(callback) {
//badSleep(3000);
callback();
}
http.createServer(function(request, response) {
var pathname = url.parse(request.url).pathname;
console.log("Serve up " + pathname);
if (pathname == '/favicon.ico') {
response.writeHead(404);
response.end();
} else {
asyncWrapper(function() {
badSleep(3000);
response.writeHead(200, {"Content-Type": "text/plain"});
response.write("\nI was wrong " + new Date());
response.end();
});
}
}).listen(8888);
The thing is, no matter where I put the sleep, it still blocks the node event loop. The callback does not solve the blocking problem. The good users of SO told me this also in the comments.
So how does exec do it??? I was baffled, so I went and looked at the child-process code at github. I found out that exec calls spawn!!! It makes a child process! Mystery solved. The async code does not 'solve' the blocking issue, the spawn does.
That leads me to my question. Does express somehow solve the blocking problem or do you still have to worry about it?
Someone posted a comment about understanding-the-node-js-event-loop. Yes exactly. You can issue a blocking call if you wrap it in an asynchronous call, because you are not blocking the node event loop.
If you wrap a synchronous call with an asynchronous call you are still going to run into blocking. For example, if you write something like this:
fs.readFile("file1.txt", function(err, data1) {
var data2 = fs.readFileSync("file2.txt");
});
The process won't be blocked when it reads file1.txt since you are using an async call, however, as soon as it is done reading file1 and it reaches the line where it reads file2 then it will block.
By issuing a synchronous/blocking call inside an asynchronous/non-blocking call you are only delaying the blocking.
You are correct that blocking for the entire web site is really bad which is why you should not issue blocking calls very frequently. Since node.js was written from the ground up most of the I/O calls are asynchronous by default and you should use those as much as possible instead of synchronous calls.
The question is, does express handle this for you automatically or do you still have to worry about it?
You still have to worry about it.
The question is, does express handle this for you automatically or do you still have to worry about it?
You still have to worry about it. NodeJS is single-threaded, which means that every synchronous operation will block it entirely, no matter where it is called. Neither Express nor any other framework can use synchronous operations without blocking the server. Simple
var x = 1;
already blocks entire server until it finishes creating new variable and assigning new value to it.
The whole point of asynchronous architecture is that it is more efficient then threads. And don't be fooled, asynchronous programming is more difficult then threads because there is no isolation. If one thread fails other still work, while in asynchronous server one exception can break entire server.
The problem is that you could block the main node event loop!
This sentence suggests that NodeJS has something more then the main event loop. That's not true. Every code is called inside the main loop.
Also have a look at this:
Any call to a non-asynchronous function will "block", even if it's wrapped in another function. The only exception is if the wrapper function can defer processing to another thread/process (e.g., like the cluster API).