notgiorgi notgiorgi - 17 days ago 5
Node.js Question

Node/express request handling model

I always thought of

Node.js/Express.js
route handlers as similar to client side
EventListeners
like
onClick
,
onHover
, etc.

Like if you do:



document
.getElementById('btn')
.addEventListener('click', function() {
setTimeout(function() {
console.log('click processed')
}, 3000)
})

<button id="btn">Click me</button>





And click button N times, it won't take
N * 3000
mss for Nth click to be processed. They will be processed almost in parallel.

So I thought If you do this in nodejs:



/*
* Assuming we have express instance `app` listening to some port
*/

app.get('/foo', function(req, res) {
setTimeout(function() {
res.send('FOO')
}, 3000)
})

app.get('/bar', function(req, res) {
setTimeout(function() {
res.send('bar')
}, 3000)
})





And request the same endpoint N times, it will process them almost in parallel like client-side event handler would do. But appears it will take approx.
N * 3000
mss for Nth request on the same endpoint to process.
On different endpoints it works just like in a client-side.

The question is: Why does nodejs handle events in different way? What am I misunderstanding and how nodejs event-loop works.

rsp rsp
Answer

Note: This answer was originally written for the original question. It was updated when the question changed slightly.

This is exactly how you should think about the requests - the request handler is just like a click handler, and different routes are like different buttons.

In your example Express handles them both requests in parallel. If you make two requests in a short period of time then you will also get answers close to each other.

You haven't shown how you actually do your requests and this is what I suspect is the problem here.

Example

Here is a Node program - the same as your example but also prints the current time in the response:

var app = require('express')();

app.get('/foo', function (req, res) {
  setTimeout(function () {
    res.send('foo ' + (new Date).toISOString() + '\n');
  }, 3000);
});

app.get('/bar', function (req, res) {
  setTimeout(function () {
    res.send('bar ' + (new Date).toISOString() + '\n');
  }, 3000);
});

app.listen(3000, function () {
  console.log('listening on:\n' +
              'http://localhost:3000/foo\n' +
              'http://localhost:3000/bar');
});

Save it in app.js, install Express with npm install express and start it with node app.js.

Now, run two requests at the same time using two different endpoints:

curl http://localhost:3000/foo & curl http://localhost:3000/bar

And you will see something like this:

[1] 15181
bar 2016-11-17T11:45:31.134Z
foo 2016-11-17T11:45:31.136Z
[1]+  Done                    curl http://localhost:3000/foo

It shows 2 milliseconds difference in response, not 3 seconds.

The same is true if you connect to the same endpoint twice:

curl http://localhost:3000/foo & curl http://localhost:3000/foo

You will get:

[1] 18636
foo 2016-11-17T13:52:19.448Z
foo 2016-11-17T13:52:19.448Z
[1]+  Done                    curl http://localhost:3000/foo

Of course if you run this - for two different endpoints:

curl http://localhost:3000/foo ; curl http://localhost:3000/bar

You will get:

foo 2016-11-17T11:47:08.085Z
bar 2016-11-17T11:47:11.103Z

Because the requests were not made in parallel this time.

The same is true for accessing the same endpoint twice:

curl http://localhost:3000/foo ; curl http://localhost:3000/foo

You will get:

foo 2016-11-17T13:53:19.208Z
foo 2016-11-17T13:53:22.227Z

Possible problem

You didn't specify it in the question but if your requests come from the browser then what you experience may be a limit of parallel connection that the browser does. Traditionally it has been solved by using several subdomains - it's sometimes called domain sharding.

Update

It doesn't matter if it's the same endpoint or two endpoints that are being hit twice at the same time, just like it wouldn't matter if the same button or two different buttons were clicked and registered timeouts in the browser. Try changing the invocation of my examples and you're see for yourself.

Comments