How to create a sleep/delay in nodejs that is Blocking?
我目前正在尝试学习nodejs,而我正在做的一个小项目正在编写一个API,以控制一些联网的LED灯。
控制LED的微处理器具有处理延迟,我需要将发送给微控制器的命令间隔至少100毫秒。 在C#中,我习惯于仅调用Thread.Sleep(time),但在node中没有找到类似的功能。
我发现了在节点中使用setTimeout(...)函数的几种解决方案,但是,这是异步的并且不会阻塞线程(这是我在此方案中需要的)。
有人知道阻塞睡眠或延迟功能吗? 最好是不只是旋转CPU并具有+ -10 ms的精度的东西?
Node本质上是异步的,这就是它的优点,所以您实际上不应该阻塞线程,但是由于这似乎是用于控制LED的项目,因此无论如何我都会发布一个工作参数,即使它不是好一个,不应该使用(严重)。
while循环将阻塞线程,因此您可以创建自己的睡眠功能
1 2 3 4 5 6 7 | function sleep(time, callback) { var stop = new Date().getTime(); while(new Date().getTime() < stop + time) { ; } callback(); } |
用作
1 2 3 | sleep(1000, function() { // executes after one second, and blocks the thread }); |
我认为这是阻塞线程的唯一方法(原则上),使线程处于循环状态,因为Node没有内置任何阻塞功能,因为这一定会破坏异步行为的目的。
最好的解决方案是为您的LED创建单例控制器,该控制器将排队所有命令并以指定的延迟执行它们:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 | 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(); |
现在您可以调用
1 2 3 | Led.exec(cmd, function() { console.log('Command sent'); }); |
使用Node睡眠包。 https://www.npmjs.com/package/sleep。
在您的代码中,您可以使用
1 2 | var sleep = require('sleep'); sleep.sleep(n) |
睡眠特定的n秒。
只需使用
1 2 3 4 5 6 7 8 9 10 11 | //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等到连接网络驱动器后再运行其余应用程序。
借助ECMA脚本2017(受Node 7.6及更高版本支持),它成为了一种形式:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | 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") }); } |
我想出的最简单的真正同步解决方案(即没有收益/异步),可以在所有OS上运行而没有任何依赖关系,那就是调用节点进程来评估嵌入式
1 2 | const sleep = (ms) => require("child_process") .execSync(`"${process.argv[0]}" -e setTimeout(function(){},${ms})`); |
我发现这里的东西几乎可以正常工作https://stackoverflow.com/questions/21819858/how-to-wrap-async-function-calls-into-a-sync-function-in-node-js-or-ja
文字
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | `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);` |
唯一的问题是打印日期不正确,但该过程至少是顺序的。
使用本机插件实现非常简单,因此有人这样做:https://github.com/ErikDubbelboer/node-sleep.git
即使开发严格的硬件解决方案,也不必在Node.js中进行阻塞。请参阅不使用
异步调用
-
ping 命令是跨平台的 -
start /b 表示:启动程序但
不显示窗口。
代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 | 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)) |
注意重要事项:以上并非精确的解决方案,它只是接近阻塞时间
您可以简单地使用ECMA6和
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | 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和回调。