Очень быстрая бесконечная петля без блокировки ввода / вывода

Есть ли более быстрая альтернатива 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">&nbsp;</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 должно быть проще и более гибким.

Другие вопросы по тегам