node js выполняет несколько команд параллельно? - PullRequest
0 голосов
/ 10 апреля 2020

Выполняет ли node js несколько команд параллельно или выполняет одну команду (и, наконец, sh это!), А затем выполняет вторую команду?

Например, если несколько асин c функций используйте один и тот же стек, и они наберут sh и выскочит "вместе", могу ли я получить странное поведение?

1 Ответ

0 голосов
/ 10 апреля 2020

Node.js запускает ваш основной Javascript (исключая созданные вручную рабочие потоки на данный момент) как один поток. Таким образом, одновременно выполняется только одна часть вашего Javascript.

Но когда запрос сервера содержит асинхронные операции, в этом дескрипторе запроса происходит то, что он запускает асинхронную операцию и затем возвращает управление вернуться к переводчику. Асинхронная операция выполняется самостоятельно (обычно в собственном коде). Пока все это происходит, интерпретатор JS может go вернуться к событию l oop и выбрать следующее событие, ожидающее запуска. Если это еще один входящий запрос для вашего сервера, он захватит этот запрос и начнет его выполнять. Когда он попадает в асинхронную операцию и возвращается обратно к интерпретатору, интерпретатор затем возвращается к событию l oop для следующего события, ожидающего запуска. Это может быть либо другой входящий запрос, либо это может быть одна из предыдущих асинхронных операций, которая теперь готова к выполнению своего обратного вызова.

Таким образом, node.js продвигается вперед по нескольким запросам за раз, которые включают асинхронные операции (такие как работа в сети, запросы к базе данных, операции с файловой системой и т. д. c ...) при одновременном запуске только одного фрагмента Javascript за раз.

Начиная с узла v10.5, nodejs имеет рабочие потоки. Они еще не используются системой автоматически при обычном обслуживании сетевых запросов, но вы можете создавать свои собственные рабочие потоки и запускать некоторое количество Javascript в действительно параллельном потоке. Это, вероятно, не нужно для кода, который в основном связан с вводом / выводом, потому что асинхронный характер ввода / вывода в Javascript уже дает ему много параллелизма. Но если у вас были операции с интенсивным использованием ЦП (интенсивное шифрование, анализ изображений, сжатие видео и т. Д. c ..., что было сделано в Javascript), рабочие потоки, безусловно, стоит добавить для этих конкретных задач.

Чтобы показать вам пример, давайте рассмотрим два обработчика запросов: один, который читает файл с диска, а другой - некоторые данные из конечной точки сети.

 app.get("/getFileData", (req, res) => {
      fs.readFile("myFile.html", function(err, html) {
         if (err) {
             console.log(err);
             res.sendStatus(500);
         } else {
             res.type('html').send(html);
         }
      })
 });

 app.get("/getNetworkData", (req, res) => {
     got("http://somesite.com/somepath").then(result => {
         res.json(result);
     }).catch(err => {
         console.log(err);
         res.sendStatus(500);
     });
 });

В запросе /getFileData Вот последовательность событий:

  1. Клиент отправляет запрос на http://somesite.com/getFileData
  2. Входящее сетевое событие обрабатывается ОС
  3. Когда node.js попадает на событие l oop, он видит событие для входящего TCP-соединения на порту, который прослушивает его http-сервер, и вызывает обратный вызов для обработки этого запроса
  4. Библиотека http в node.js получает этот запрос, анализирует его и уведомляет наблюдателей об этом запросе, одним из которых будет Express framework
  5. . Express framework сопоставляет этот запрос с указанным выше обработчиком запроса и вызывает запрос h andler
  6. Этот обработчик запросов начинает выполняться и вызывает fs.readFile("myfile.html", ...). Поскольку это асинхронно, вызов функции просто инициирует процесс (выполняя первые шаги), регистрирует обратный вызов для его завершения, а затем немедленно возвращает.
  7. На этом этапе вы можете видеть из этого обработчика запроса /getFileData что после вызова fs.readFile() обработчик запроса просто возвращается. До тех пор, пока не будет вызван обратный вызов, ему больше нечего делать.
  8. Возвращает управление обратно к событию nodejs l oop, где nodejs может выбрать следующее событие, ожидающее запуска и выполнения.

В запросе /getNetworkData приведена последовательность событий

Шаги 1-5 такие же, как и выше. 6. Обработчик запросов начинает выполняться и вызывает got("http://somesite.com/somepath"). Это инициирует запрос к этой конечной точке, а затем немедленно возвращает обещание. Затем регистрируются обработчики .then() и .catch() для отслеживания этого обещания. 7. На этом этапе из этого обработчика запроса /getNetworkData видно, что после вызова got().then().catch() обработчик запроса просто возвращается. Пока обещание не будет решено или отклонено, ему больше нечего делать. 8. Это возвращает управление обратно к событию nodejs l oop, где nodejs может выбрать следующее событие, ожидающее запуска и выполнить его.

Теперь, когда-нибудь в будущем, fs.readFile("myFile.html", ...) завершится , На этом этапе некоторая внутренняя подсистема (которая может использовать другие потоки собственного кода) вставляет событие завершения в node.js событие l oop.

Когда node.js возвращается к событию l oop, он увидит это событие и выполнит обратный вызов завершения, связанный с операцией fs.readFile(). Это запустит остальную часть логики c в этом обработчике запросов.

Затем, когда-нибудь в будущем, сетевой запрос из got("http://somesite.com/somepath") завершится, и это вызовет событие в событии l oop для вызова обратного вызова завершения для этой сетевой операции. Этот обратный вызов разрешит или отклонит обещание, которое вызовет обратные вызовы .then() или .catch(), а второй запрос выполнит оставшуюся часть его логики c.


Надеюсь, вы Из этих примеров видно, как обработчики запросов инициируют асинхронную операцию, а затем возвращают управление обратно интерпретатору, где интерпретатор может затем извлечь следующее событие из события l oop и запустить его. Затем, когда асинхронные операции завершаются, в событие l oop вставляются другие элементы, вызывающие дальнейший прогресс для каждого обработчика запросов, пока в конце концов они не завершат свою работу. Таким образом, несколько разделов кода могут прогрессировать без одновременного выполнения более одного фрагмента кода. Это по сути совместная многозадачность, в которой временное разделение между операциями происходит на границах асинхронных операций, а не автоматическое упреждающее временное срезание c в полностью поточной системе.

Nodejs получает число преимуществ от этого типа многозадачности, так как это намного, намного меньше накладных расходов (совместное переключение задач намного эффективнее, чем автоматическое переключение задач c), и оно также не имеет большей части обычной синхронизации потоков проблемы, которые делают настоящие многопоточные системы, что может сделать их намного более сложными для кода и / или более подверженными сложным ошибкам.

...