Очень быстрая бесконечная петля без блокировки ввода / вывода
Есть ли более быстрая альтернатива window.requestAnimationFrame()
для бесконечных циклов, которые не блокируют ввод / вывод?
То, что я делаю в цикле, не связано с анимацией, поэтому мне все равно, когда следующий кадр готов, и я прочитал это window.requestAnimationFrame()
ограничен частотой обновления монитора или, по крайней мере, ждет пока не будет нарисован кадр.
Я также попробовал следующее:
function myLoop() {
// stuff in loop
setTimeout(myLoop, 4);
}
(4, потому что это минимальный интервал в setTimeout
и меньшие значения будут по умолчанию равны 4.) Однако мне нужно лучшее разрешение, чем это.
Есть ли что-нибудь с еще лучшей производительностью?
Мне в основном нужна неблокирующая версия while(true)
,
2 ответа
Две вещи, которые будут выполняться раньше, чем это setTimeout
:
process.nextTick
обратные вызовы (специфичные для NodeJS):process.nextTick()
Метод добавляет обратный вызов в "следующую очередь тиков". Как только текущий ход поворота цикла событий завершится, будут вызваны все обратные вызовы, которые в данный момент находятся в следующей очереди тиков.Это не простой псевдоним
setTimeout(fn, 0)
, Это гораздо эффективнее. Он запускается до запуска любых дополнительных событий ввода-вывода (включая таймеры) в последующих тиках цикла событий.Обещание расчетных уведомлений
Таким образом, это могут быть инструменты для вашего пояса: сочетание одного или обоих изsetTimeout
чтобы достичь баланса, который вы хотите.
Подробности:
Как вы, вероятно, знаете, данный поток JavaScript выполняется на основе очереди задач (спецификация называет это очередью заданий); и, как вы, наверное, знаете, в браузерах есть один основной поток пользовательского интерфейса по умолчанию, а NodeJS запускает один поток.
Но на самом деле в современных реализациях есть как минимум две очереди задач: главная, о которой мы все думаем (гдеsetTimeout
и обработчики событий помещают свои задачи), и очередь "микрозадач", в которую помещаются определенные асинхронные операции во время обработки основной задачи (или "макрозадачи"). Эти микрозадачи обрабатываются, как только завершается выполнение макрозадачи,перед следующей макрозадачей в главной очереди, даже если эта следующая макрозадача была поставлена в очередь до выполнения микрозадач.
nextTick
Обратные вызовы и уведомления об урегулировании обещаний являются микрозадачами. Таким образом, планирование либо планирует асинхронный обратный вызов, но тот, который произойдет до следующей основной задачи.
Мы можем видеть это в браузере сsetInterval
и цепочка разрешения обещаний:
let counter = 0;
// setInterval schedules macrotasks
let timer = setInterval(() => {
$("#ticker").text(++counter);
}, 100);
// Interrupt it
$("#hog").on("click", function() {
let x = 300000;
// Queue a single microtask at the start
Promise.resolve().then(() => console.log(Date.now(), "Begin"));
// `next` schedules a 300k microtasks (promise settlement
// notifications), which jump ahead of the next task in the main
// task queue; then we add one at the end to say we're done
next().then(() => console.log(Date.now(), "End"));
function next() {
if (--x > 0) {
if (x === 150000) {
// In the middle; queue one in the middle
Promise.resolve().then(function() {
console.log(Date.now(), "Middle");
});
}
return Promise.resolve().then(next);
} else {
return 0;
}
}
});
$("#stop").on("click", function() {
clearInterval(timer);
});
<div id="ticker"> </div>
<div><input id="stop" type="button" value="Stop"></div>
<div><input id="hog" type="button" value="Hog"></div>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
Когда вы запустите это и нажмете кнопку Hog, обратите внимание на то, как замерзает дисплей счетчика, а затем продолжает работать снова. Это из-за 300000 микрозадач, которые запланированы заранее. Также обратите внимание на временные метки в трех сообщениях журнала, которые мы пишем (они не появляются в консоли фрагмента, пока их не отобразит макротаска, но временные метки показывают нам, когда они были зарегистрированы).
Таким образом, в принципе, вы можете запланировать кучу микрозадач и периодически позволять им заканчиваться и запускать следующую макрозадачу.
Примечание: я использовал setInterval
для примера браузера во фрагменте, но setInterval
в частности, не может быть хорошим выбором для аналогичного эксперимента с использованием NodeJS, так как NodeJS setInterval
немного отличается от того, что в браузерах, и имеет некоторые неожиданные временные характеристики.
Есть некоторые библиотеки, которые могут работать как задачи cron, например, https://www.npmjs.com/package/node-cron
я думаю, что использование cron должно быть проще и более гибким.