2

Node.js is famous for concurrency, however, I'm confused by how to make it work concurrently. I started two requests from Chrome one by one very quickly, and I Expected the outputs in console should be:

  1. "get a new request"
  2. immediately after my second request, "get a new request" should be printed
  3. after several seconds, "end the new request"
  4. after several seconds, "end the new request"

However, what I saw is:

  1. "get a new request"
  2. after several seconds, "end the new request"
  3. "get a new request"
  4. after several seconds, end the new request

That means the second request is NOT handled until the first one is done. Below is my sample code, anything I missed?

   var http = require("http");
   var url = require("url");

   function start(route) {
    http.createServer(function(request, response) {
      console.log('get a new request');

      // a time consuming loop           
      for (var i=0; i<10000000000; ++i) {
      }

      route(url.parse(request.url).pathname);
      response.writeHead(200, {"Content-Type": "text/plain"});
      response.end();
      console.log('end the new request');
    }).listen(5858);
   }

   function saySomething(something)  {
     console.log(something);
   }

   exports.start = start;
   exports.saySomething = saySomething;
buaaji
  • 111
  • 10
  • 1
    Take a look at http://code.tutsplus.com/tutorials/node-js-for-beginners--net-26314 for a tutorial and at http://stackoverflow.com/questions/6898779/how-to-write-asynchronous-functions-for-node-js for a possible answer – randunel Feb 20 '14 at 13:25
  • node.js is famed for its asynchronous model, I think, not its concurrent model. The "server" in this case should handle callbacks. – Rui Vieira Feb 20 '14 at 13:25

4 Answers4

3

You don't have to do anything.

It's based on non blocking I/O. Put simply, there is an event loop. A certain set of sync code are run, once done, the next iteration is run that picks up the next set of sync code to run. Anytime an async op is run (db fetch, setTimeout, reading a file, etc) the next tick of the event loop is run. This way there is never any code just waiting.

It's not threaded. In your example, the for loop is in one continuous chunk of code, so js will run the entire for loop before it can handle another http request.

Try putting a setTimeout around the for loop so that node can switch to the next event loop and in your case handle a web request.

pbo
  • 537
  • 4
  • 13
  • So, for example, there are 10 requests in the event loop, the first one is de-queued from the loop and handled by the sever, and if there are some time-consuming I/O operations, the first request will go to sleep until the I/O is done and it's queued to the end of the event loop. Because the first request is queued again, the 2nd request then get a chance to be handled, is that correct? – buaaji Feb 20 '14 at 13:46
  • It's not about how long the ops take. That is the part that is up to you. When every the code defers execution, another tick of the event loop is run. – pbo Feb 20 '14 at 13:52
  • So if you know you have a function that is expensive, you could batch it in groups and defer each group. Easiest way to do this is with setTimeout. – pbo Feb 20 '14 at 13:53
  • When ever you make any io the handler you specify will run on a future tick. Node does not wait for io to complete. – pbo Feb 20 '14 at 13:54
  • 1
    Putting `setTimeout` around the loop will _not_ change anything; it will still be blocking all other events while it's running. Only way to block less would be to split the loop into batches, with `setImmediate` between each batch, or spawning a new process (Worker). Also neither `setTimeout(fn, 0)` nor `process.nextTick` would work since they do not yield to the event loop. – Andreas Hultgren Feb 20 '14 at 14:58
  • Yeah, if the loop costs too much time, a single setTimeOut would also block the other events. One possible way is that: if the callback function in setTimeOut is too long, setup the 2nd setTimeOut in the callback, setup the 3rd setTimeOut and so on. – buaaji Feb 20 '14 at 15:22
  • Yes of course, the point was to show how js queues the code. I already mentioned batching. – pbo Feb 20 '14 at 17:22
  • I figured it out. The for loop is a cpu block, you can 'bypass' this by using threads. See my updated answer – GiveMeAllYourCats Jul 09 '14 at 20:52
2

node can't handle these:

for (var i=0; i<10000000000; ++i) {}

concurrently. But it handles IO concurrently

vkurchatkin
  • 13,364
  • 2
  • 47
  • 55
1

You might want to look at Clusters:

http://nodejs.org/api/cluster.html#cluster_how_it_works

http://rowanmanning.com/posts/node-cluster-and-express/

Aurélien Thieriot
  • 5,853
  • 2
  • 24
  • 25
-1

T̶h̶i̶s̶ ̶i̶s̶ ̶t̶h̶e̶ ̶e̶x̶p̶e̶c̶t̶e̶d̶ ̶b̶e̶h̶a̶v̶i̶o̶r̶,̶ ̶w̶e̶ ̶c̶a̶l̶l̶ ̶t̶h̶i̶s̶ ̶̶b̶l̶o̶c̶k̶i̶n̶g̶̶.̶ ̶T̶h̶e̶ ̶s̶o̶l̶u̶t̶i̶o̶n̶ ̶f̶o̶r̶ ̶h̶a̶n̶d̶l̶i̶n̶g̶ ̶c̶o̶n̶c̶u̶r̶r̶e̶n̶t̶ ̶r̶e̶q̶u̶e̶s̶t̶ ̶i̶s̶ ̶m̶a̶k̶i̶n̶g̶ ̶t̶h̶e̶ ̶c̶o̶d̶e̶ ̶̶n̶o̶n̶-̶b̶l̶o̶c̶k̶i̶n̶g̶̶.̶ ̶A̶s̶ ̶s̶o̶o̶n̶ ̶a̶s̶ ̶y̶o̶u̶ ̶c̶a̶l̶l̶e̶d̶ ̶̶r̶e̶s̶p̶o̶n̶s̶e̶.̶w̶r̶i̶t̶e̶H̶e̶a̶d̶̶ ̶t̶h̶e̶ ̶c̶o̶d̶e̶ ̶b̶e̶g̶a̶n̶ ̶b̶l̶o̶c̶k̶i̶n̶g̶ ̶w̶a̶i̶t̶i̶n̶g̶ ̶f̶o̶r̶ ̶̶r̶e̶s̶p̶o̶n̶s̶e̶.̶e̶n̶d̶̶.

EDIT 7/8/14: Had to deal with this problem recently and found out you can use threads for this: https://www.npmjs.org/package/webworker-threads

Webworker-threads provides an asynchronous API for CPU-bound tasks that's missing in Node.js:

var Worker = require('webworker-threads').Worker;
require('http').createServer(function (req,res) {
  var fibo = new Worker(function() {
    function fibo (n) {
      return n > 1 ? fibo(n - 1) + fibo(n - 2) : 1;
    }
    this.onmessage = function (event) {
      postMessage(fibo(event.data));
    }
  });
  fibo.onmessage = function (event) {
    res.end('fib(40) = ' + event.data);
  };
  fibo.postMessage(40);
}).listen(port);

And it won't block the event loop because for each request, the fibo worker will run in parallel in a separate background thread.

GiveMeAllYourCats
  • 890
  • 18
  • 23
  • Thank you for your explanation, however, your solution can't solve my problem, the output sequence doesn't change. Just like [pbo](http://stackoverflow.com/users/688940/pbo) said, setTimeout could do the trick. – buaaji Feb 20 '14 at 14:40
  • setTimeout or a function can both work in these instances, the question is do you want the code to directly execute using a function or execute it with a delay using a setTimeout. The point is that blocking code should be in different event loop whether this is via setTimeout or function. – GiveMeAllYourCats Feb 20 '14 at 14:52
  • I don't understand... why the function timeConsumingLoop can defer the execution of the busy loop? I tried your code, and the outputs are: get/end, get/end, get/end... – buaaji Feb 20 '14 at 15:10
  • @buaaji I have updated my answer, I came across a solution I had to use recently. – GiveMeAllYourCats Jul 08 '14 at 16:14
  • 1
    @MDG this is possible, but not a good idea: it will consume a lot of memory under heavy load, threads are not lightweight (because of isolates). You can't share anything (=no closures), functions are actually passed via `toString`, also no `require` and other node apis. It's easier to set up a bunch of node processes as workers and distribute jobs with something like 0mq. – vkurchatkin Jul 10 '14 at 18:29