66

我目前正在尝试学习 nodejs,我正在做的一个小项目正在编写一个 API 来控制一些联网的 LED 灯。

控制 LED 的微处理器有一个处理延迟,我需要将发送到微控制器的命令间隔至少 100 毫秒。在 C# 中,我习惯于只调用 Thread.Sleep(time),但我在 node.js 中没有找到类似的功能。

我在节点中找到了几个使用 setTimeout(...) 函数的解决方案,但是,这是异步的并且不会阻塞线程(这是我在这种情况下需要的)。

有人知道阻塞睡眠或延迟功能吗?最好是不只是旋转 CPU,并且精度为 +-10 ms 的东西?

4

13 回答 13

71

Node本质上是异步的,这就是它的优点,所以你真的不应该阻塞线程,但因为这似乎是一个控制LED的项目,我还是会发布一个workaraound,即使它不是一个非常好一个,不应该使用(认真)。

一个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 没有内置任何阻塞功能,因为它会破坏异步行为的目的。

于 2014-01-07T08:42:06.297 回答
42

使用 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")
  });
}
于 2018-12-03T09:15:58.447 回答
41

最好的解决方案是为您的 LED 创建单例控制器,它将所有命令排队并以指定的延迟执行它们:

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');
});
于 2014-01-07T09:36:13.967 回答
35

使用节点睡眠包。https://www.npmjs.com/package/sleep

在您的代码中,您可以使用

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

睡眠特定的 n 秒。

于 2015-08-28T06:35:26.203 回答
32

只需使用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 等到连接网络驱动器后再运行应用程序的其余部分。

于 2016-03-12T03:32:03.970 回答
6

最简单的真正同步解决方案(即无产量/异步)我能想出的适用于所有操作系统而没有任何依赖关系的方法是调用节点进程来评估内联setTimeout表达式:

const sleep = (ms) => require("child_process")
    .execSync(`"${process.argv[0]}" -e setTimeout(function(){},${ms})`);
于 2018-04-30T10:32:47.050 回答
2

使用本机插件实现非常简单,所以有人这样做了:https ://github.com/ErikDubbelboer/node-sleep.git

于 2014-01-07T11:51:55.207 回答
2

我在这里发现了一些几乎可以工作的东西https://stackoverflow.com/questions/21819858/how-to-wrap-async-function-calls-into-a-sync-function-in-node-js-or-ja vascript

`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);`

唯一的问题是打印的日期不正确,但该过程至少是连续的。

于 2016-09-13T08:55:00.013 回答
1

即使在开发紧凑的硬件解决方案时,也不需要在 Node.js 中进行阻塞。请参阅不使用或setImmediate 的 temporal.js 相反,它使用or which 提供更高分辨率的任务执行,并且您可以创建任务的线性列表。但是你可以在不阻塞线程的情况下做到这一点。setTimeoutsetIntervalsetImmediatenextTick

于 2014-12-12T18:43:43.897 回答
1

异步调用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))

注意重要:以上不是精确解决方案,它只是接近阻塞时间

于 2019-10-23T11:00:26.447 回答
0

您可以简单地使用yieldECMA6 和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!");
});
于 2018-01-21T18:40:31.453 回答
0

正如睡眠包作者建议的*

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

function sleep(n) {
  msleep(n * 1000);
}
于 2022-01-19T04:45:07.547 回答
-5

阻塞主线程对于节点来说不是一种好的风格,因为在大多数情况下,不止一个人在使用它。您应该将 settimeout/setinterval 与回调结合使用。

于 2014-01-07T08:34:01.900 回答