Как setTimeout работает в Node.JS?

112

Я предполагаю, что после выполнения он находится в очереди, но есть ли в очереди гарантия, что он будет активирован ровно через X миллисекунд? Или другие тяжелые задачи, стоящие выше в очереди, задержат его?

MyCodeGone
источник
14
Никакая операционная система, не работающая в реальном времени, никогда не даст гарантии серьезной точности. Всевозможные вещи в системе могут (и будут) мешать механизму таймера.
Pointy

Ответы:

174

Семантика setTimeout примерно такая же, как и в веб-браузере: аргумент тайм-аута - это минимальное количество мс для ожидания перед выполнением, а не гарантия. Кроме того, передача 0, нечисла или отрицательного числа заставит его ждать минимальное количество мс. В Node это 1 мс, но в браузерах оно может достигать 50 мс.

Причина этого в том, что JavaScript не вытесняет JavaScript. Рассмотрим этот пример:

setTimeout(function () {
  console.log('boo')
}, 100)
var end = Date.now() + 5000
while (Date.now() < end) ;
console.log('imma let you finish but blocking the event loop is the best bug of all TIME')

Поток здесь такой:

  1. запланируйте тайм-аут на 100 мс.
  2. busywait на 5000 мс.
  3. вернуться в цикл событий. проверьте наличие ожидающих таймеров и выполните.

Если бы это было не так, тогда один бит JavaScript мог бы «прерывать» другой. Нам пришлось бы настроить мьютексы, семафоры и тому подобное, чтобы не допустить, чтобы такой код был чрезвычайно трудным для рассуждений:

var a = 100;
setTimeout(function () {
  a = 0;
}, 0);
var b = a; // 100 or 0?

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

Разве это лучший демон для битвы, чем сложность упреждения? Это зависит.

Айзекс
источник
20
Вы получаете +1 за исключительно точное console.logсообщение.
Иск Фонда Моники
Мне нравится, как вы прописываете ВРЕМЯ в строке. Очень хорошее объяснение !!!
Alisson
43

Идея неблокирования заключается в том, что итерации цикла выполняются быстро. Таким образом, итерация для каждого тика должна занимать достаточно короткое время, чтобы setTimeout был точным с разумной точностью (с отклонением, может быть, <100 мс или около того).

Хотя теоретически ты прав. Если я напишу заявку и заблокирую галочку, то setTimeouts будет задерживаться. Итак, чтобы ответить на ваш вопрос, кто может гарантировать своевременное выполнение setTimeouts? Вы, написав неблокирующий код, можете контролировать степень точности почти до любой разумной степени точности.

Пока javascript является «однопоточным» с точки зрения выполнения кода (исключая веб-рабочих и т.п.), это всегда будет происходить. Однопоточная природа - это огромное упрощение в большинстве случаев, но для успеха требуется неблокирующая идиома.

Попробуйте использовать этот код в своем браузере или в узле, и вы увидите, что нет никакой гарантии точности, наоборот, setTimeout будет очень поздно:

var start = Date.now();

// expecting something close to 500
setTimeout(function(){ console.log(Date.now() - start); }, 500);

// fiddle with the number of iterations depending on how quick your machine is
for(var i=0; i<5000000; ++i){}

Если интерпретатор не оптимизирует цикл (чего нет в chrome), вы получите что-то тысячи. Снимите петлю, и вы увидите, что на носу 500 ...

Дэвин
источник
9

Единственный способ обеспечить выполнение кода - поместить логику setTimeout в другой процесс.

Используйте модуль дочернего процесса, чтобы создать новую программу node.js, которая выполняет вашу логику и передает данные этому процессу через какой-то поток (возможно, tcp).

Таким образом, даже если в вашем основном процессе выполняется какой-то длинный блокирующий код, ваш дочерний процесс уже запустился и поместил setTimeout в новый процесс и новый поток и, таким образом, будет выполняться, когда вы этого ожидаете.

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

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

Raynos
источник
9

setTimeoutэто своего рода поток , он удерживает операцию в течение заданного времени и выполняет.

setTimeout(function,time_in_mills);

здесь первый аргумент должен быть типом функции; Например, если вы хотите напечатать свое имя через 3 секунды, ваш код должен выглядеть примерно так, как показано ниже.

setTimeout(function(){console.log('your name')},3000);

Ключевой момент, о котором следует помнить, заключается в том, что все, что вы хотите сделать с помощью setTimeoutметода, делайте внутри функции . Если вы хотите вызвать какой-либо другой метод путем анализа некоторых параметров, ваш код должен выглядеть следующим образом:

setTimeout(function(){yourOtherMethod(parameter);},3000);
Ashana.Jackol
источник
8

setTimeout(callback,t)используется для выполнения обратного вызова по крайней мере через t миллисекунды . Фактическая задержка зависит от многих внешних факторов, таких как степень детализации таймера ОС и загрузка системы.

Таким образом, есть вероятность, что он будет вызван немного позже установленного времени, но никогда не будет вызван раньше.

Таймер не может работать более 24,8 дней.

Шива Пракаш
источник