我如何在Node.js (JavaScript)等待?我需要暂停一段时间

我正在为个人需求开发一个控制台脚本。我需要能够暂停更长的时间,但是,根据我的研究,Node.js没有办法根据需要停止。一段时间后,读取用户信息变得越来越困难了……我已经看到了一些代码,但我相信他们必须有其他的代码在他们的工作,如:

    setTimeout(function() {
}, 3000);

但是,我需要这行代码之后的所有内容在一段时间之后执行。

例如,

    // start of code
console.log('Welcome to my console,');


some-wait-code-here-for-ten-seconds...


console.log('Blah blah blah blah extra-blah');
// end of code

我还见过

    yield sleep(2000);

但是Node.js不能识别这个。

我怎样才能实现这种延长的暂停?

1192714 次浏览

把你想要在延迟后执行的代码放在setTimeout回调中:

console.log('Welcome to My Console,');
setTimeout(function() {
console.log('Blah blah blah blah extra-blah');
}, 3000);

最好的方法是把你的代码分解成多个函数,像这样:

function function1() {
// stuff you want to happen right away
console.log('Welcome to My Console,');
}


function function2() {
// all the stuff you want to happen after that pause
console.log('Blah blah blah blah extra-blah');
}


// call the first chunk of code right away
function1();


// call the rest of the code and have it execute after 3 seconds
setTimeout(function2, 3000);

它类似于JohnnyHK的解决方案,但是更整洁,更容易扩展。

由于javascript引擎(v8)是基于事件队列中的事件顺序运行代码的,因此没有严格的规定javascript在指定的时间后准确地触发执行。也就是说,当您设置几秒后执行代码时,触发代码完全基于事件队列中的顺序。因此,触发代码执行的时间可能会超过指定的时间。

node。js遵循,

process.nextTick()

来代替setTimeout()运行代码。例如,

process.nextTick(function(){
console.log("This will be printed later");
});

这个问题很老了,但是最近V8增加了生成器,可以完成OP的请求。在暂停gen-run等库的帮助下,生成器通常最容易用于异步交互。

下面是一个使用suspend的例子:

suspend(function* () {
console.log('Welcome to My Console,');
yield setTimeout(suspend.resume(), 10000); // 10 seconds pass..
console.log('Blah blah blah blah extra-blah');
})();

相关阅读(通过无耻的自我宣传):发电机有什么大不了的?

我最近创建了一个更简单的抽象,名为wait.for,以同步模式(基于节点光纤)调用异步函数。还有一个基于即将到来的ES6生成器的版本。

https://github.com/luciotato/waitfor < a href = " https://github.com/luciotato/waitfor " > < / >

使用wait.for,你可以调用任何标准的nodejs async函数,就像它是一个同步函数一样,而不会阻塞节点的事件循环。

您可以在需要时按顺序编码,这(我猜)完美地简化了您的脚本供个人使用。

使用wait.for你的代码将是:

require('waitfor')


..in a fiber..
//start-of-code
console.log('Welcome to My Console,');
wait.miliseconds(10*1000); //defined in waitfor/paralell-tests.js - DOES NOT BLOCK
console.log('Blah blah blah blah extra-blah');
//endcode.
此外,任何async函数都可以在同步模式下调用。

.

.

你可以使用www.npmjs.com/package/sleep

var sleep = require('sleep');
sleep.sleep(10); // sleep for ten seconds

这是一个简单的阻塞技巧:

var waitTill = new Date(new Date().getTime() + seconds * 1000);
while(waitTill > new Date()){}

它是阻塞,因为在脚本中不会发生任何其他事情(比如回调)。但由于这是一个控制台脚本,也许它就是您所需要的!

有关更多信息

yield sleep(2000);

你应该检查Redux-Saga。但是它是特定于您选择Redux作为模型框架的(尽管严格来说不是必需的)。

在阅读了这个问题的答案后,我把一个简单的函数放在一起,如果你需要的话,它也可以做一个回调:

function waitFor(ms, cb) {
var waitTill = new Date(new Date().getTime() + ms);
while(waitTill > new Date()){};
if (cb) {
cb()
} else {
return true
}
}

在Linux/nodejs上,这对我来说是有效的:

const spawnSync = require('child_process').spawnSync;

var sleep = spawnSync('sleep', [1.5]);

它正在阻塞,但它不是一个繁忙等待循环。

您指定的时间以秒为单位,但可以是分数。我不知道其他操作系统是否有类似的命令。

如果你只是为了测试而挂起当前线程执行,试试这个:

function longExecFunc(callback, count) {


for (var j = 0; j < count; j++) {
for (var i = 1; i < (1 << 30); i++) {
var q = Math.sqrt(1 << 30);
}
}
callback();
}
longExecFunc(() => { console.log('done!')}, 5); //5, 6 ... whatever. Higher -- longer

Node 7.6.0或更高版本

节点支持本地等待:

const sleep = (waitTimeInMs) => new Promise(resolve => setTimeout(resolve, waitTimeInMs));

然后如果你可以使用异步函数:

await sleep(10000); // sleep for 10 seconds

或者:

sleep(10000).then(() => {
// This will execute 10 seconds from now
});

在较旧的Node版本上(原始答案)

我想要一个异步睡眠工作在Windows &Linux,不占用我的CPU与长while循环。我尝试了睡眠包,但它无法安装在我的Windows盒子上。我最终使用:

https://www.npmjs.com/package/system-sleep

要安装它,输入:

npm install system-sleep

在你的代码中,

var sleep = require('system-sleep');
sleep(10*1000); // sleep for 10 seconds

效果非常好。

2021年1月更新:你甚至可以在节点REPL交互中使用--experimental-repl-await标志

$ node --experimental-repl-await
> const delay = ms => new Promise(resolve => setTimeout(resolve, ms))
> await delay(1000) /// waiting 1 second.
老问题的新答案。今天(2019年6月1日2017年1月)要容易得多。你可以使用新的async/await语法。 例如:< / p >
async function init() {
console.log(1);
await sleep(1000);
console.log(2);
}


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

为了在没有安装和插件的情况下使用async/await,你必须使用node-v7或node-v8,使用--harmony标志。

2019年6月更新:通过使用最新版本的NodeJS,你可以开箱即用。不需要提供命令行参数。即使谷歌Chrome今天也支持它。

2020年5月更新: 很快,您就可以在异步函数之外使用await语法了。在顶层,就像这个例子中的

await sleep(1000)
function sleep(ms) {
return new Promise((resolve) => {
setTimeout(resolve, ms);
});
}
< p > # EYZ0。 今天你可以使用webpack 5 (alpha),

更多信息:

let co = require('co');
const sleep = ms => new Promise(res => setTimeout(res, ms));


co(function*() {
console.log('Welcome to My Console,');
yield sleep(3000);
console.log('Blah blah blah blah extra-blah');
});

上面的代码是解决Javascript的异步回调地狱问题的副作用。这也是我认为Javascript在后端成为有用语言的原因。实际上,在我看来,这是引入到现代Javascript的最令人兴奋的改进。为了充分理解它的工作原理,需要充分理解发电机的工作原理。function关键字后面跟着*,在现代Javascript中称为生成器函数。npm包co提供了一个运行生成器的运行器函数。

本质上,生成器函数提供了一种使用yield关键字暂停函数执行的方法,同时,生成器函数中的yield使得在生成器内部和调用者之间交换信息成为可能。这为调用方提供了一种机制,可以从异步调用的promise中提取数据,并将已解析的数据传递回生成器。实际上,它使异步调用同步化。

其他的答案都很好,但我想我要采取不同的策略。

如果你真正想要的只是在linux中减慢一个特定的文件:

 rm slowfile; mkfifo slowfile; perl -e 'select STDOUT; $| = 1; while(<>) {print $_; sleep(1) if (($ii++ % 5) == 0); }' myfile > slowfile  &

节点myprog slowfile

这将每5行休眠1秒。节点程序将和写入程序一样慢。如果它在做其他事情,它们将继续以正常速度运行。

mkfifo创建先进先出管道。这就是它的作用。 perl行可以像你想要的那样快。$|=1表示不缓冲输出。< / p >

简单,我们将等待5秒钟来等待一些事件发生(这将由done变量在代码中的其他地方设置为true表示),或者当超时到期时,我们将每100ms检查一次

    var timeout=5000; //will wait for 5 seconds or untildone
var scope = this; //bind this to scope variable
(function() {
if (timeout<=0 || scope.done) //timeout expired or done
{
scope.callback();//some function to call after we are done
}
else
{
setTimeout(arguments.callee,100) //call itself again until done
timeout -= 100;
}
})();

对于一些人来说,公认的答案是行不通的,我找到了另一个答案,它对我来说是有效的:如何将参数传递给setTimeout()回调?

var hello = "Hello World";
setTimeout(alert, 1000, hello);

'hello'是正在传递的参数,您可以在超时时间后传递所有参数。感谢@Fabio Phms的回答。

由于ES6支持# eyz0,我们可以在没有任何第三方帮助的情况下使用它们。

const sleep = (seconds) => {
return new Promise((resolve, reject) => {
setTimeout(resolve, (seconds * 1000));
});
};


// We are not using `reject` anywhere, but it is good to
// stick to standard signature.

然后这样使用它:

const waitThenDo(howLong, doWhat) => {
return sleep(howLong).then(doWhat);
};

注意,doWhat函数变成了new Promise(...)中的resolve回调。

还要注意,这是异步睡眠。它不会阻塞事件循环。如果你需要阻塞睡眠,使用这个库,它在c++绑定的帮助下实现了阻塞睡眠。(尽管在Node中像异步环境一样需要阻塞睡眠的情况很少。)

https://github.com/erikdubbelboer/node-sleep

使用现代Javascript的简单而优雅的睡眠函数

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

没有依赖,没有回调;就是这样:-)


考虑到问题中给出的例子,这是我们在两个控制台日志之间睡眠的方式:

async function main() {
console.log("Foo");
await sleep(2000);
console.log("Bar");
}


main();

“缺点”是你的主函数现在也必须是async。但是,考虑到您已经在编写现代Javascript代码,您可能(或者至少应该!)在整个代码中使用async/await,所以这真的不是一个问题。所有现代浏览器今天支持它。

对于那些不习惯使用async/await和胖箭头操作符的人,稍微了解一下sleep函数,这是详细的书写方式:

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

但是,使用胖箭头操作符会使它更小(也更优雅)。

如果你想“编码高尔夫”;你可以在这里对其他一些答案做一个简短的版本:

const sleep = ms => new Promise(resolve => setTimeout(resolve, ms));

但在我看来,真正理想的答案是使用Node的util库和它的promisify函数,它正是为这类事情而设计的(为先前存在的非承诺的东西制作基于承诺的版本):

const util = require('util');
const sleep = util.promisify(setTimeout);

在任何一种情况下,你都可以通过使用await来调用你的sleep函数来暂停:

await sleep(1000); // sleep for 1s/1000ms

正如评论中提到的,你甚至可以把它减少到一行:

const sleep = require('util').promisify(setTimeout);

或者,如果你甚至不想费心创建一个sleep函数:

await require('util').promisify(setTimeout)(1000);

这是一个moment.js风格的模块,基于@atlex2建议的脏阻塞方法。# EYZ1。

const moment = require('moment');


let sleep = (secondsToSleep = 1) => {
let sleepUntill = moment().add(secondsToSleep, 'seconds');
while(moment().isBefore(sleepUntill)) { /* block the process */ }
}


module.exports = sleep;

没有任何依赖关系的最短解决方案:

await new Promise(resolve => setTimeout(resolve, 5000));
function doThen(conditional,then,timer) {
var timer = timer || 1;
var interval = setInterval(function(){
if(conditional()) {
clearInterval(interval);
then();
}
}, timer);
}

使用示例:

var counter = 1;
doThen(
function() {
counter++;
return counter == 1000;
},
function() {
console.log("Counter hit 1000"); // 1000 repeats later
}
)

尝试使用promise,它在NodeJS中很管用

一个衬套

await new Promise(resolve => setTimeout(resolve, 5000));

或者将它作为NodeJS中的一个函数来重用

const sleep = async (milliseconds) => {
await new Promise(resolve => setTimeout(resolve, milliseconds));
}

像这样使用函数

await sleep(5000)

为了“等待”;在javascript使用承诺的方式去顶部的答案显示。

那么如何使用它呢?

下面是一个简单的例子,一个5秒的子进程以非阻塞的方式为一个4秒的主进程排队参数。

const wait = (seconds) =>
new Promise(resolve =>
setTimeout(() =>
resolve(true), seconds * 1000))


const process = async (items, prepTask, mainTask) => {
const queue = [];
let done = false;


items.forEach((item, i) => {
prepTask(item).then(() => {
queue.push(item);
if (i == items.length -1) {
done = true;
}
})
})


while (!done || queue.length) {
if (queue.length) {
const workload = queue.shift();
await mainTask(workload)
} else {
console.log('waiting for subtask to queue')
await wait(1);
}
}
}


// Usage Example


const ids = [1,2,3,4,5,6,7,8,9,10];


const prepTask = async (id) => {
await wait(id * 5)
return id * 5;
}


const mainTask = async (workload) => {
console.log('excuting workload: ', workload);
const result = await wait(4);
return { workload, result }
}


process(ids, prepTask, mainTask)
.then(() => console.log('done'))

从Node.js 15及以上版本,您可以使用Timers承诺API。您不必再承诺setTimeout或依赖第三方库。

import { setTimeout } from 'timers/promises';


await setTimeout(1000);

节点16有一种新的方法来轻松地做到这一点

import { setTimeout } from 'timers/promises'


console.log('before')
await setTimeout(3000)
console.log('after')