Почему цикл while блокирует цикл событий?

Следующий пример приведен в книге Node.js:

var open = false; setTimeout(function() { open = true }, 1000) while (!open) { console.log('wait'); } console.log('open sesame'); 

Объясняя, почему цикл while блокирует выполнение, автор говорит:

Узел никогда не выполнит обратный вызов тайм-аута, потому что цикл события застрял на этом, а цикл запущен в строке 7, никогда не давая ему возможности обработать событие тайм-аута!

Однако автор не объясняет, почему это происходит в контексте цикла событий или того, что действительно происходит под капотом.

Может ли кто-то уточнить это? Почему узел застревает? И как изменить код выше, сохраняя при while структуру управления, чтобы цикл события не блокировался, и код будет вести себя так, как можно было бы ожидать. wait будет занесен в журнал только за 1 секунду до того, как будет setTimeout и затем процесс завершится после регистрации «открытого кунжута».

Общие объяснения, такие как ответы на этот вопрос об IO и циклах событий и обратных вызовах, действительно не помогают мне рационализировать это. Я надеюсь, что ответ, который непосредственно ссылается на приведенный выше код, поможет.

На самом деле это довольно просто. Внутренне node.js состоит из этого типа цикла:

  • Получить что-то из очереди событий
  • Запустите любую задачу и запустите ее, пока она не вернется
  • Когда вышеуказанная задача будет выполнена, получите следующий элемент из очереди событий
  • Запустите любую задачу и запустите ее, пока она не вернется
  • Полоскание, пена, повторение – снова и снова

Если в какой-то момент в очереди событий ничего нет, перейдите в режим сна, пока в очереди событий не появится что-то.


Итак, если часть Javascript сидит в то while() , то эта задача не заканчивается и в приведенной выше последовательности ничего нового не будет выбрано из очереди событий до тех пор, пока эта предварительная задача не будет полностью выполнена. Таким образом, очень длинный или вечно бегущий while() цикл просто подтягивает работы. Поскольку Javascript запускает только одну задачу за один раз (однопоточный для выполнения JS), если эта одна задача вращается в цикле while, тогда ничего не может быть выполнено.

Вот простой пример, который мог бы объяснить это:

  var done = false; // set a timer for 1 second from now to set done to true setTimeout(function() { done = true; }, 1000); // spin wait for the done value to change while (!done) { /* do nothing */} console.log("finally, the done value changed!"); 

Некоторые могут логически полагать, что цикл while будет вращаться, пока не заработает таймер, а затем таймер изменит значение done на true а затем цикл while завершится, и console.log() в конце будет выполнена. Это НЕ, что произойдет. На самом деле это будет бесконечный цикл, и оператор console.log() никогда не будет выполнен.

Проблема в том, что, как только вы войдете в ожидание вращения в цикле while() , НИКАКИЙ другой Javascript не сможет выполнить. Таким образом, таймер, который хочет изменить значение done переменной, не может выполнить. Таким образом, условие цикла while никогда не может измениться и, следовательно, это бесконечный цикл.

Вот что происходит внутри механизма JS:

  1. done переменная, инициализированная значением false
  2. setTimeout() запускает событие таймера в течение 1 секунды
  3. Цикл while начинает вращаться
  4. 1 секунда во вращающемся цикле while, таймер срабатывает внутри двигателя JS, и обратный вызов таймера добавляется в очередь событий. Вероятно, это происходит в другом streamе, внутреннем для JS-движка.
  5. Цикл while продолжает вращаться, потому что переменная done никогда не изменяется. Поскольку он продолжает вращаться, JS-движок никогда не заканчивает этот stream выполнения и никогда не сможет вытащить следующий элемент из очереди событий.

Узел представляет собой единую последовательную задачу. Нет параллелизма, и его параллелизм связан с IO. Подумайте об этом так: все работает на одном streamе, когда вы делаете вызов IO, который блокирует / синхронно, ваш процесс останавливается до тех пор, пока данные не будут возвращены; однако говорят, что у нас есть единственный stream, который вместо ожидания на IO (чтение диска, захват URL-адреса и т. д.) ваша задача переходит к следующей задаче, и после завершения этой задачи она проверяет, что IO. Это в основном то, что делает узел, его «цикл событий» – его опрос IO для завершения (или прогресса) в цикле. Поэтому, когда задача не завершается (ваш цикл), цикл событий не прогрессирует. Проще говоря.

Это отличный вопрос, но я нашел исправление!

 var sleep = require('system-sleep') var done = false setTimeout(function() { done = true }, 1000) while (!done) { sleep(100) console.log('sleeping') } console.log('finally, the done value changed!') 

Я думаю, что это работает, потому что system-sleep – это не ожидание вращения.

потому что таймер должен вернуться и ждать цикла, чтобы закончить, чтобы добавить в очередь, поэтому, хотя тайм-аут находится в отдельном streamе и может действительно завершить таймер, но «задача» для установки done = true ожидает этого бесконечного цикла заканчивать

  • Локальная зависимость в package.json
  • Как подключить суб-приложения express.js?
  • Не удается найти модуль 'angleular2 / angular2'
  • Как добавить файл в узел?
  • Любой способ заставить строгий режим в узле?
  • Заменить строку в файле с помощью nodejs
  • npm ошибки установки с ошибкой: ENOENT, chmod
  • NPM global install "не может найти модуль"
  • Обновление Node.js до последней версии
  • Как сделать сервер webpack dev запущенным на порту 80 и 0.0.0.0, чтобы сделать его общедоступным?
  • Заseleniumие Mongoose vs вложенность объекта
  • Interesting Posts

    Каков метод замещения этого устаревшего вызова MagicalRecord?

    Каков наилучший тип данных для использования в c #?

    Может ли controller AngularJS наследовать от другого controllerа в том же модуле?

    Как делать снимки с камеры без предварительного просмотра, когда начнется мое приложение?

    Компьютер действительно медленный

    Спящий режим из меню «Пуск» без отключения гибридного сна?

    eventlisteners, использующие hibernate 4.0 с весной 3.1.0.release?

    поиск и замена целого слова

    Могу ли я создать загрузочный USB-флеш-накопитель Linux с помощью компьютера под управлением Windows?

    Встроенные ярлыки в Matplotlib

    Android обнаружит, что нажатие клавиши «Готово» для клавиатуры OnScreen

    Как добавить время отладки к асинхронному валидатору в угловом 2?

    Лучший способ parseDouble с запятой в качестве десятичного разделителя?

    Как добавить индексное поле к результатам Linq

    «Произошла ошибка с запуском выбранного генератора кода» в лесах VS 2013

    Давайте будем гением компьютера.