Как работает цикл событий Node.js?
После игры с Node.js и прочтения об асинхронном вводе-выводе и вечернем программировании у меня остались некоторые вопросительные знаки.
Рассмотрим следующий (псевдо) код:
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
(Я думаю) я понимаю основы цикла событий; С помощью libev Node.js создает цикл обработки событий, который опрашивает (epoll/kqueue/...) набор файловых дескрипторов, чтобы увидеть, инициированы ли какие-либо события (новое соединение, возможность записи, доступные данные и т. Д.). Если есть новый запрос, цикл обработки событий вызывает анонимную функцию, переданную createServer. Я не понимаю, что происходит после:
1) Для одновременного выполнения запросов у драйвера БД должен быть какой-то пул потоков / соединений, верно?
2) В рамках одного запроса: что происходит после отправки двух запросов? renderView не может быть вызван, потому что запросы еще не возвращены. Как мы ждем возвращения запросов? Должен ли он вести подсчет обратных вызовов, ожидающих запуска перед продолжением? Основная мысль у меня была;
onRequest -> выполнить асинхронный код -> ждать обратных вызовов -> создать ответ. Ожидание в этом случае будет блокировать, поэтому вам нужно будет создать поток для каждого onRequest. Как выполняется "ожидание выполнения обратных вызовов перед построением ответа"?
3) Как драйвер БД информирует цикл обработки событий о том, что он завершен, и обратный вызов, который он имеет для этого, должен вызываться с результатами запроса?
4) Как цикл обработки событий выполняет обратный вызов внутри анонимной функции, которую мы создали с помощью события onRequest? В этом ли заключается концепция замыкания, когда контекст "сохраняется" в функции обратного вызова?
4) Теперь, когда у нас есть результаты БД, как мы продолжим выполнять renderView/res.write/res.end
части?
2 ответа
Запустите шаблон параллельного асинхронного кода:
Чтобы "дождаться результата от обеих асинхронных функций", вы можете сделать: в обоих асинхронных вызовах обратные вызовы проверяют оба результата и, если все готово, вызывают ваш DoSomethingWithTwoDependantResults.
В вашем примере вам, вероятно, нужно выполнять запросы последовательно:
query(sql1, function(sqlres1) {
query(sql2, function(sqlres2) {
writeResultUsingDataFrom(sqlres1, sqlres2);
}
});
Ваш исходный код, модифицированный для параллельного выполнения двух запросов:
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);
});
};