如何在阻塞的 nodejs 中创建睡眠/延迟?

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

控制发光二极管的微处理器有一个处理延迟,我需要向微处理器发送至少相隔100毫秒的空间命令。在 C # 中,我习惯于只调用 Thread。睡眠(时间) ,但是我没有在节点中找到类似的特性。

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

是否有人知道阻碍睡眠或延迟功能?最好是一些不只是旋转的 CPU,并具有 +-10毫秒的精度?

198351 次浏览

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

Node 本质上是异步的,这就是它的伟大之处,所以你真的不应该阻塞线程,但由于这似乎是一个项目控制 LED 的,我将发布一个变通方案,即使它不是一个非常好的和 不应该被使用(严肃地说)。

While 循环将阻塞线程,因此您可以创建自己的 sleep 函数

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 没有任何内置的阻塞功能,因为它会在某种程度上破坏异步行为的目的。

最好的解决方案是为你的 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');
});

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

在 Node.js 中阻塞是不必要的,即使在开发紧凑的硬件解决方案时也是如此。请参阅 暂时的 JS,它不使用 setTimeoutsetInterval即时。相反,它使用 setImmediatenextTick,它们提供了更高分辨率的任务执行,并且您可以创建一个任务的线性列表。但是你可以在不阻塞线程的情况下做到这一点。

使用节点睡眠包。

在您的代码中可以使用

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

睡一个特定的 n 秒。

只要使用 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 等待,直到网络驱动器附加完毕,然后再运行应用程序的其余部分。

我在这里找到了一些几乎可以工作的东西 < a href = “ https://stackoverflow. com/questions/21819858/how-to-wrapc-function-call-into-a-sync-function-in-node-js-or-ja% 20vascript”> https://stackoverflow.com/questions/21819858/how-to-wrap-async-function-calls-into-a-sync-function-in-node-js-or-ja 瓦斯特

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

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

您可以简单地使用 ECMA6和 gen-run库中引入的 yield特性:

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表达式:

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

有了 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")
});
}

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

重要告示: 以上并不是一个精确的解决方案,它只是接近阻塞时间

正如睡眠包的作者所建议的 *:

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


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