Почему setTimeout не отменяет мой цикл?

Я задавался вопросом, сколько раз инструкция JavaScript while (в консоли Chrome) может увеличивать переменную за миллисекунду, поэтому я быстро написал этот фрагмент непосредственно в консоль:

var run = true, i = 0;
setTimeout(function(){ run = false; }, 1);
while(run){ i++; }

Проблема в том, что она работает вечно. Почему это происходит, и как я могу его решить?

Ответ 1

Это возвращается к однопоточному характеру JavaScript 1. Что происходит в значительной степени:

  • Ваши переменные назначены.
  • Вы планируете функцию, чтобы установить run = false. Это запланировано для запуска после текущей функции (или что-то еще в настоящее время активно).
  • У вас есть бесконечный цикл и оставайтесь внутри текущей функции.
  • После бесконечного цикла (никогда) будет выполнен обратный вызов setTimeout() и run=false.

Как вы можете видеть, подход setTimeout() не работает здесь. Вы можете обойти это, проверив время в while, но это повлияет на ваше фактическое измерение.

1 По крайней мере, для более практических целей вы можете видеть это как однопоточное. На самом деле существует так называемый "цикл событий". В этом цикле все функции попадают в очередь до их выполнения. Если вы ставите в очередь новую функцию, она помещается в соответствующую позицию внутри этой очереди. После того, как текущая функция завершена, движок выполняет следующую функцию из очереди (относительно введенных значений времени, например, setTimeout() и выполняет ее.
В результате в каждый момент времени выполняется только одна функция, что делает выполнение в значительной степени однопоточным. Существуют некоторые исключения для событий, которые обсуждаются в приведенной ниже ссылке.


Для справки:

  • fooobar.com/questions/75661/...

    Здесь более подробно описан подобный сценарий

  • fooobar.com/questions/1106/...

    Более подробное описание того, когда JS можно рассматривать как однопоточное, а когда нет.

Ответ 2

JavaScript является однопоточным, поэтому, пока вы находитесь в цикле, ничего не запускается.

Ответ 3

Чтобы сохранить истинную скорость работы Chrome без необходимости постоянно получать время для вычисления скорости, вы можете попробовать этот код JS:

var start = new Date().getTime()
var i = 0
while(i<1000000)
{
    i++
}
var end = new Date().getTime()
var delta = end-start
var speed = i/delta
console.log(speed + " loops per millisecond")

Ответ 4

Javascript является однопоточным, что означает, что он запускает только одну команду за раз, последовательно.

Система событий, как и во многих других языках и в библиотеке, является обработкой циклом события. Цикл событий в основном представляет собой цикл, который на каждой итерации проверяет сообщение в очереди и передает события.

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

Ваша "настоящая" программа выглядит примерно так:

var run = true, i = 0;
setTimeout(function(){ run = false; }, 1);
while(run){ i++; }

while(true) {
/*
 * check for new messages in the queue and dispatch
 * events if there are some
 */
  processEvents();
}

Таким образом, сообщение с тайм-аутом, указывающим часы, никогда не обрабатывается.

Дополнительная информация о цикле событий: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/EventLoop


Конечно, это немного сложнее, проверьте здесь эти примеры: Является ли JavaScript гарантированным однопоточным? ( tl; dr: В некоторых браузерах некоторые внешние события не зависят от цикла событий и сразу же запускаются, когда они возникают, вытесняя текущую задачу. Но это не относится к setTimeout, которые просто добавляют сообщение в очередь и никогда не срабатывают немедленно.)

Ответ 5

Цикл While не имеет доступа к setTimeout. У вас есть код, который устанавливает run true, а затем он никогда не станет false.