Разрешить запуск setInterval более одного раза в миллисекунду в nodejs

У меня есть сценарий узла, который должен использовать все ресурсы ЦП, которые может получить один узел. Но я нашел setInterval слишком медленным.

И, конечно же, я нашел это в документации:

Когда задержка больше, чем 2147483647 или меньше 1, задержка будет установлена равной 1.

источник: https://nodejs.org/api/timers.html#timers_setinterval_callback_delay_args

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

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

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

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

Что это значит?

Рассмотрим несколько примеров:

setInterval(()=>{console.log('a')},1000) // asynchronous thing that needs to run in the background

while (true) {
    // do whatever
}

Что будет делать этот код? Это заблокирует все. console.log('a') не будет выполняться непрерывно.

setInterval(()=>{console.log('a')},1000) // asynchronous thing that needs to run in the background
setTimeout(()=>{
    while (true) {
        // do whatever
    }
}, 1)

Это также заблокирует выполнение интервалов, как только начнется цикл while.

Ответ 1

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

setImmediate

setImmediate - расписание "немедленного" выполнения обратного вызова после обратных вызовов событий ввода-вывода. Возвращает Immediate для использования с clearImmediate().

Когда выполняются множественные вызовы setImmediate(), функции обратного вызова ставятся в очередь для выполнения в том порядке, в котором они созданы. Вся очередь обратного вызова обрабатывается итерацией цикла событий. Если непосредственный таймер поставлен в очередь из исполняемого обратного вызова, этот таймер не будет запущен до следующей итерации цикла событий.

Это из направляющих node:

setImmediate и setTimeout похожи, но ведут себя по-разному в зависимости от того, когда они вызывают.

  • setImmediate() предназначен для выполнения сценария после завершения текущей фазы опроса.
  • setTimeout() запускает скрипт, который будет запущен после истечения минимального порога в мс.

process.nextTick

Метод process.nextTick() добавляет обратный вызов в "очередную тиковую очередь". Как только текущий поворот цикла цикла события завершится, все вызовы, вызываемые в настоящее время в следующей очереди тиков, будут вызваны.

Из направляющей node

Мы рекомендуем разработчикам использовать setImmediate() во всех случаях, потому что это легче рассуждать (и это приводит к коду, совместимому с более широким спектром сред, например браузером JS).

Ответ 2

1 setInterval несколько раз запускается больше!

let count = 0,
  by = 100,
  _intervals = [],
  timelimit = 100
for (let i = 0; i < by; i++) {
  _intervals[i] = setInterval(() => count++, 1)
}
setTimeout(() => {
  _intervals.forEach(x => clearInterval(x))
  console.log('count:${count}')
}, timelimit)

Ответ 3

Благодаря Джошу Лин за идею просто запустить несколько интервалов. Я закончил с двумя простыми функциями обертки для setInterval и clearInterval:

function setInterval2(cb,delay) {
    if (delay >= 1)
        return [setInterval(cb,delay)];
    var intervalArr = [];
    var intervalCount = Math.round(1/delay);
    for (var i=0; i<intervalCount; i++)
        intervalArr.push(setInterval(cb,1));
    return intervalArr
}

function clearInterval2(intervalArr) {
    intervalArr.forEach(clearInterval);
}

Он работает так же, как и исходные функции:

var count = 0;

// run interval every 0.01 milliseconds:
var foo = setInterval2(function(){
    count++;
},0.01);

// stop execution:
clearInterval2(foo)

Ответ 4

Вы спрашиваете, возможно ли

запустите setInterval более одного раза в миллисекунду в nodejs

Как вы заметили в своем вопросе, это невозможно с setInterval, так как всегда существует минимальная задержка не менее 1 мс в node.js. В браузерах часто существует минимальная задержка не менее 10 мс.

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

Как отмечено в ответе The Reason, setImmediate - хороший вариант, доступный в node.js. Поскольку setImmediate имеет ограниченную поддержку браузера и вряд ли будет широко поддерживаться в будущем, есть и другой подход, который также работает в браузерах.

В то время как браузеры обеспечивают минимальную задержку для setInterval и setTimeout, задержка для setTimeout выполняется с момента установки таймера, а не при ее запуске. Если мы многократно используем setTimeout для вызова кода с интенсивным использованием ЦП, мы можем убедиться, что таймер всегда установлен на 10-15 мс заранее (если для выполнения кода требуется не менее 10-15 мс), тем самым уменьшая фактическую задержку до 0 Миз.

Демонстрационный фрагмент ниже выдает код из этого ответа, чтобы продемонстрировать, как использование таймеров, установленных заранее, может сделать задержку меньше, чем то, что предусмотрено. В проверенных вами браузерах это обычно приводит к задержке 0 мс.

// First: repeat runCPUForAtLeast50ms() 10 times
// with standard repeated setTimeouts and enforced delays
testTimeout(10, function(){
  // Then: repeat runCPUForAtLeast50ms() 10 times
  // using a repeated set of queued setTimeouts
  // circumventing the enforced delays
  testTimeout(10, false, true);
});

function testTimeout(repetitions, next, multiple) {
  var delays = [];
  var lastCheck;
  var extraTimers;

  function runner() {
    if(lastCheck){
      delays.push((+new Date) - lastCheck);
    }
    if(repetitions > 0) {
      //process chunk
      runCPUForAtLeast50ms();
      //set new timer
      setTimeout(runner);
    } else if(repetitions === 0) {
      //report result to console
      console.log((multiple? 
        'Repeated multiple timers delays: ' : 
        'Repeated single timer delays: ') + delays.join(', '));
      //invoke next() function if provided
      next && next();
    }
    repetitions--;
    lastCheck = +new Date;
  }

  setTimeout(runner);

  if(multiple){
   // make sure that there are always a fixed
   // number of timers queued by setting extra timers
   // at start
   extraTimers = 10;
   while(extraTimers--)
     setTimeout(runner);
  }
}

function runCPUForAtLeast50ms() {
  var d = (+new Date) + 50;
  while(+new Date < d);
}

Ответ 5

Я думаю, что вы можете решить свою проблему, используя асинхронный модуль... способ может быть:

async.parallel([
  (callback) => {
    // do normal stuff
  },
  (callback) => {
    // do your loop
  }
], (err, results) => {
  // ...
});

Но учтите это примечание из официальной документации...

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

Ответ 6

Короче говоря, вы не можете. Существует некоторое ограничение Javascript/Node, так как это приложение с одним потоком. Вот почему у вас асинхронные прерывания.

Длинный ответ: с точки зрения компьютерной архитектуры современное планирование CPU и ядра не является детерминированным. Если вам нужен такой контроль над зерном, я бы предложил вам посмотреть MCU и встроенные решения, у которых нет планировщика ядра. Потому что у вашей ОС есть много других процессов и процессов ядра, которые занимают процессорное время, поэтому планировщик ядра должен планировать различные процессы, которые должны выполняться на процессоре, и удовлетворить множество разных требований.

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

Теперь, если вы хотите использовать все ресурсы ЦП, это невозможно.

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

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