Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How does the Node.js event loop work?

After playing with Node.js and reading about async i/o & evented programming a lot I'm left with some question marks.

Consider the following (pseudo) code:

var http = require('http');

function onRequest(request, response)
{
    // some non-blocking db query
    query('SELECT name FROM users WHERE key=req.params['key']', function (err, results, fields) {
        if (err) {
            throw err;
        }
        username = results[0];
    });

    // some non-blocking db query
    query('SELECT name FROM events WHERE key=req.params['key']', function (err, results, fields) {
        if (err) {
            throw err;
        }
        event_name = results[0];
    });

    var body = renderView(username, event_name, template);
    res.writeHead(200, {'Content-Type': 'text/plain'});
    res.write(body);
    res.end();
};

http.createServer(onRequest).listen(8888);

// request A: http://127.0.0.1:1337/?key=A
// request B: http://127.0.0.1:1337/?key=B

(I think) I understand the basics of the event loop; With libev, Node.js creates an event loop that polls (epoll/kqueue/...) a bunch of file descriptors to see if any events are triggered (new connection, writable, data available etc). If there is a new request the event loop calls the anonymous function passed to createServer. What I don't understand is what happens after:

1) To run the queries concurrently the db driver has to have some kind of threading/connection pool, right?

2) In the scope of one request: What happens after sending two queries? renderView can't be called because the queries have not returned yet. How do we wait for the queries to return? Should it keep count of the callbacks pending to be fired before continuing? The basic thought I had was;

onRequest -> run async code -> wait for callbacks -> construct response. The waiting in this case would be blocking so you would effectively need to spawn a thread for each onRequest. How is the "waiting for callbacks to run before constructing response" done?

3) How does the db driver inform the event-loop that it's done and the callback it has for it needs to be called with the query results?

4) How does the event loop run the callback inside the anonymous function we created with the onRequest event? Is this where the closure concept comes in where the context is "saved" in the callback function?

4) Now that we have the db results, how do we continue executing the renderView/res.write/res.end parts?

like image 662
Justin Case Avatar asked Jul 20 '11 20:07

Justin Case


1 Answers

Run parrallel async code pattern:

To 'Wait for result from both async functions' you can do: in both async calls callbacks check both result and if all ready, call your DoSomethingWithTwoDependantResults.

In your example you probably need to execute queries sequentially:

query(sql1, function(sqlres1) {
    query(sql2, function(sqlres2) {
        writeResultUsingDataFrom(sqlres1, sqlres2);
    }
});

your original code, modified to execute two queries in parallel:

function writeReply(res, template, username, event_name)
{
    var body = renderView(username, event_name, template);
    res.writeHead(200, {'Content-Type': 'text/plain'});
    res.write(body);
    res.end();
} 

function onRequest(request, response)
{
    // some non-blocking db query
    query('SELECT name FROM users WHERE key=req.params['key']', function (err, results, fields) {
        if (err) {
            throw err;
        }
        username = results[0];
        if (username && event_name)
            writeReply(res, template, username, event_name);
    });

    // some non-blocking db query
    query('SELECT name FROM events WHERE key=req.params['key']', function (err, results, fields) {
        if (err) {
            throw err;
        }
        event_name = results[0];
        if (username && event_name)
            writeReply(res, template, username, event_name);

    });
};
like image 64
Andrey Sidorov Avatar answered Oct 19 '22 08:10

Andrey Sidorov