Node.js побудований на libuv , бібліотеці між платформами, яка абстрагує apis / syscalls для асинхронного (не блокуючого) введення / виводу, що забезпечується підтримуваними ОС (як мінімум, Unix, OS X та Windows).
Асинхронний IO
У цій моделі програмування операція відкриття / читання / запису на пристроях та ресурсах (сокетах, файловій системі тощо), керованою файловою системою , не блокує викликовий потік (як у типовій синхронній c-подібній моделі), а просто позначає процес (у структурі даних на рівні ядра / ОС), щоб повідомлятись про появу нових даних або подій. У випадку програми, схожої на веб-сервер, після цього процес відповідає за визначити, до якого запиту / контексту належить сповіщена подія та продовжити обробку запиту звідти. Зауважте, що це обов'язково означає, що ви знаходитесь в іншому кадру стека, ніж той, що створив запит до ОС, оскільки останній повинен був поступитися диспетчеру процесу, щоб єдиний потіковий процес обробляти нові події.
Проблема з моделлю, яку я описав, полягає в тому, що програмісту це не знайоме і важко міркувати, оскільки це не послідовно. "Вам потрібно зробити запит у функції A і обробити результат у іншій функції, де ваші місцеві жителі з А зазвичай недоступні."
Модель вузла (стиль проходження продовження та цикл подій)
Вузол вирішує проблему використання мовних функцій javascript, щоб зробити цю модель трохи більш синхронічною, спонукаючи програміста використовувати певний стиль програмування. Кожна функція, яка запитує IO, має такий підпис function (... parameters ..., callback)і їй потрібно надати зворотний дзвінок, який буде викликаний після завершення запитуваної операції (майте на увазі, що більшість часу витрачається на очікування, коли ОС подасть сигнал про завершення - час, який може бути витратив на виконання іншої роботи). Підтримка Javascript для закриття дозволяє використовувати змінні, визначені вами у зовнішній (викличній) функції всередині корпусу зворотного дзвінка, - це дозволяє зберігати стан між різними функціями, на які буде викликано час роботи вузла незалежно. Дивіться також Стиль проходження продовження .
Більше того, після виклику функції, що нерестить операцію вводу-виводу, функція виклику зазвичай returnконтролює цикл подій вузла . Цей цикл викликає наступний зворотний виклик або функцію, яка була запланована на виконання (швидше за все, через те, що про відповідну подію було повідомлено ОС) - це дозволяє одночасно обробляти декілька запитів.
Ви можете вважати цикл подій вузла дещо подібним до диспетчера ядра : ядро планувало б виконати заблокований потік, як тільки його очікуваний введення-вивід буде завершено, тоді як вузол планує зворотний виклик, коли відбулася відповідна подія.
Висока паралельність, паралелізму немає
На завершення зауваження, що фраза "все працює паралельно, крім вашого коду" виконує гідну роботу з захоплення точки, що вузол дозволяє вашому коду одночасно обробляти запити з сотень тисяч відкритих сокет одним потоком шляхом мультиплексування та послідовності всіх ваших js логіка в одному потоці виконання (навіть якщо сказати, що "все працює паралельно" тут, мабуть, невірно - див. Конкурс проти паралелізму. У чому різниця? ). Це працює досить добре для серверів webapp, оскільки більшість часу фактично витрачається на очікування мережі або диска (база даних / сокети), а логіка насправді не є інтенсивною процесором - тобто: це добре працює для навантажень, пов'язаних з IO .