Пам’ятайте, що, хоча JavaScript є однопотоковим, усі входи / виходи вузла та виклики власних API є або асинхронними (за допомогою механізмів, що відповідають платформі), або виконуються в окремому потоці. (Це все обробляється через libuv.)
Отже, коли є дані, доступні в сокеті, або повернулась функція власного API, нам потрібен синхронізований спосіб викликати функцію JavaScript, яка зацікавлена в конкретній події, яка щойно сталася.
Небезпечно просто викликати функцію JS з потоку, де відбулася власна подія, з тих самих причин, з якими ви стикалися б у звичайному багатопотоковому додатку - умови гонки, доступ до неатомної пам'яті тощо.
Отже, що ми робимо, це розміщуємо подію в черзі безпечним способом. У спрощеному пседокоді щось на зразок:
lock (queue) {
queue.push(event);
}
Потім, повернувшись до основного потоку JavaScript (але на стороні C), ми робимо щось на зразок:
while (true) {
lock (queue) {
var tickEvents = copy(queue);
queue.empty();
}
for (var i = 0; i < tickEvents.length; i++) {
InvokeJSFunction(tickEvents[i]);
}
}
Значок while (true)
(який насправді не існує у вихідному коді вузла; це суто ілюстративно) представляє цикл подій . Внутрішній for
викликає функцію JS для кожної події, яка була в черзі.
Це галочка: синхронне виклик нуля або більше функцій зворотного виклику, пов’язаних із будь-якими зовнішніми подіями. Як тільки черга вичерпається і повернеться остання функція, галочка закінчиться. Ми повертаємось на початок (наступний галочку) і перевіряємо наявність подій, які були додані в чергу з інших потоків під час роботи нашого JavaScript .
Що може додати речі до черги?
process.nextTick
setTimeout
/setInterval
- I / O (матеріали з
fs
, net
тощо)
crypto
процесорні функції, такі як криптопотоки, pbkdf2 та PRNG (які насправді є прикладом ...)
- будь-які власні модулі, які використовують робочу чергу libuv, щоб зробити синхронні виклики бібліотеки C / C ++ виглядають асинхронними