Как создать сон / задержку в nodejs, который блокирует?

В настоящее время я пытаюсь изучить nodejs, и небольшой проект, над которым я работаю, пишет API для управления некоторыми сетевыми светодиодами.

Микропроцессор, управляющий светодиодами, имеет задержку обработки, и мне нужно разместить команды, отправленные на микро, по крайней мере на расстоянии 100 мс. В C# я привык просто вызывать Thread.Sleep(время), но я не нашел подобную функцию в узле.

Я нашел несколько решений, использующих функцию setTimeout (...) в узле, однако, это асинхронно и не блокирует поток (что мне нужно в этом сценарии).

Кто-нибудь знает о блокировке сна или функции задержки? Желательно что-то, что не просто раскручивает процессор, а имеет точность +-10 мс?

13 ответов

Решение

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

function LedController(timeout) {
  this.timeout = timeout || 100;
  this.queue = [];
  this.ready = true;
}

LedController.prototype.send = function(cmd, callback) {
  sendCmdToLed(cmd);
  if (callback) callback();
  // or simply `sendCmdToLed(cmd, callback)` if sendCmdToLed is async
};

LedController.prototype.exec = function() {
  this.queue.push(arguments);
  this.process();
};

LedController.prototype.process = function() {
  if (this.queue.length === 0) return;
  if (!this.ready) return;
  var self = this;
  this.ready = false;
  this.send.apply(this, this.queue.shift());
  setTimeout(function () {
    self.ready = true;
    self.process();
  }, this.timeout);
};

var Led = new LedController();

Теперь вы можете позвонить Led.exec и он будет обрабатывать все задержки для вас:

Led.exec(cmd, function() {
  console.log('Command sent');
});

Узел асинхронный по своей природе, и в этом-то и прелесть, так что вам не стоит блокировать поток, но так как это похоже на проект, управляющий светодиодами, я все равно опубликую обходной путь, даже если это не очень хороший и не должен использоваться (серьезно).

Цикл while блокирует поток, поэтому вы можете создать свою собственную функцию сна

function sleep(time, callback) {
    var stop = new Date().getTime();
    while(new Date().getTime() < stop + time) {
        ;
    }
    callback();
}

использоваться как

sleep(1000, function() {
   // executes after one second, and blocks the thread
});

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

С помощью скрипта ECMA 2017 (поддерживаемого Node 7.6 и выше) он становится однострочным:

function sleep(millis) {
  return new Promise(resolve => setTimeout(resolve, millis));
}

// Usage in async function
async function test {
  await sleep(1000)
  console.log("one second has elapsed")
}

// Usage in normal function
function test2 {
  sleep(1000).then(() => {
    console.log("one second has elapsed")
  });
}

Просто используйте child_process.execSync и вызвать функцию сна системы.

//import child_process module
const child_process = require("child_process");
// Sleep for 5 seconds
child_process.execSync("sleep 5");

// Sleep for 250 microseconds
child_process.execSync("usleep 250");

// Sleep for a variable number of microseconds
var numMicroSeconds = 250;
child_process.execFileSync("usleep", [numMicroSeconds]);

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

Используйте пакет сна Node. https://www.npmjs.com/package/sleep.

в вашем коде вы можете использовать

var sleep = require('sleep'); 
sleep.sleep(n)

спать в течение определенного n секунд.

Самое простое решение для истинной синхронизации (т.е. без выхода / асинхронности), которое я мог бы придумать, которое работает во всех ОС без каких-либо зависимостей, - это вызвать процесс узла для оценки in-line setTimeout выражение:

const sleep = (ms) => require("child_process")
    .execSync(`"${process.argv[0]}" -e setTimeout(function(){},${ms})`);

Как предлагает автор пакета сна *:

      function msleep(n) {
  Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, n);
}

function sleep(n) {
  msleep(n * 1000);
}

Это довольно просто реализовать с помощью нативного дополнения, поэтому кто-то сделал это: https://github.com/ErikDubbelboer/node-sleep.git

Я нашел кое-что почти работающее здесь /questions/37267019/kak-obernut-vyizovyi-asinhronnyih-funktsij-v-funktsiyu-sinhronizatsii-v-nodejs-ili-javascript

`function AnticipatedSyncFunction(){
    var ret;
    setTimeout(function(){
        var startdate = new Date()
        ret = "hello" + startdate;
    },3000);
    while(ret === undefined) {
       require('deasync').runLoopOnce();
    }
    return ret;    
}


var output = AnticipatedSyncFunction();
var startdate = new Date()
console.log(startdate)
console.log("output="+output);`

Единственная проблема в том, что напечатанная дата неверна, но процесс, по крайней мере, последовательный.

Блокировка в Node.js не требуется, даже при разработке жестких аппаратных решений. Смотрите temporal.js, который не использует setTimeout или же setInterval setImmediate. Вместо этого он использует setImmediate или же nextTick которые дают гораздо более высокое разрешение выполнения задач, и вы можете создать линейный список задач. Но вы можете сделать это, не блокируя поток.

Асинхронный вызов ping команда для блокировки выполнения текущего кода в указанные миллисекунды.

  • ping команда кроссплатформенная
  • start /b означает: запускать программу, но не показывать окно.

код, как показано ниже:

const { execSync } = require('child_process')
// delay(blocking) specified milliseconds
function sleep(ms) {
    // special Reserved IPv4 Address(RFC 5736): 192.0.0.0
    // refer: https://en.wikipedia.org/wiki/Reserved_IP_addresses
    execSync(`start /b ping 192.0.0.0 -n 1 -w ${ms} > nul`)
}

// usage
console.log("delay 2500ms start\t:" + (new Date().getTime() / 1000).toFixed(3))
sleep(2500)
console.log("delay 2500ms end\t:" + (new Date().getTime() / 1000).toFixed(3))

Обратите внимание: это не точное решение, оно просто приближается к времени блокировки

Вы можете просто использовать yield функция введена в ECMA6 и gen-run библиотека:

let run = require('gen-run');


function sleep(time) {
    return function (callback) {
        setTimeout(function(){
            console.log(time);
            callback();
        }, time);
    }
}


run(function*(){
    console.log("befor sleeping!");
    yield sleep(2000);
    console.log("after sleeping!");
});

Блокировка основного потока не является хорошим стилем для узла, потому что в большинстве случаев его используют более одного человека. Вы должны использовать settimeout/setinterval в сочетании с обратными вызовами.

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