Ответ 1
Помните, что, хотя JavaScript является однопоточным, все операции ввода/вывода node и вызовы для собственных API-интерфейсов являются либо асинхронными (с использованием механизмов, специфичных для платформы), либо выполняются в отдельном потоке. (Все это обрабатывается через libuv.)
Поэтому, когда возвращаемые данные, доступные в сокете или встроенной функции API, нам нужен синхронизированный способ вызвать функцию JavaScript, которая интересуется конкретным событием, которое только что произошло.
Небезопасно просто вызвать функцию JS из потока, в котором произошло родное событие по тем же причинам, с которыми вы столкнулись в регулярном многопоточном приложении – условия гонки, неатомный доступ к памяти и т.д.
Итак, что мы делаем, помещаем событие в очередь поточно-безопасным способом. В упрощенном psuedocode что-то вроде:
lock (queue) {
queue.push(event);
}
Затем, вернемся к основному потоку JavaScript (но на стороне C вещей), мы делаем что-то вроде:
while (true) {
// this is the beginning of a tick
lock (queue) {
var tickEvents = copy(queue); // copy the current queue items into thread-local memory
queue.empty(); // ..and empty out the shared queue
}
for (var i = 0; i < tickEvents.length; i++) {
InvokeJSFunction(tickEvents[i]);
}
// this the end of the tick
}
while (true)
(который фактически не существует в исходном коде node, это просто иллюстративно) представляет цикл событий. Внутренний for
вызывает функцию JS для каждого события, которое находилось в очереди.
Это галочка: синхронный вызов нулевой или более функций обратного вызова, связанных с любыми внешними событиями. Как только очередь будет опустошена и последняя функция вернется, галочка завершена. Мы возвращаемся к началу (следующий тик) и проверяем события, которые были добавлены в очередь из других потоков во время работы нашего JavaScript.
Что может добавить вещи в очередь?
-
process.nextTick
-
setTimeout
/<Т26 > - I/O (материал от
fs
,net
и т.д.) -
crypto
Процессорные функции, такие как криптопотоки, pbkdf2 и PRNG (которые на самом деле являются примером...) - любые встроенные модули, которые используют рабочую очередь libuv, чтобы синхронные вызовы библиотеки C/С++ выглядели асинхронными