我如何在Node.js(Javascript)中等待,我需要暂停一段时间


385

我正在为个人需求开发类似脚本的控制台。我需要能够暂停很长一段时间,但是,根据我的研究,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');
//endcode. 

我也看过类似的东西

yield sleep(2000);

但是node.js无法识别这一点。

我如何才能实现这种长时间的暂停?


3
您的情况如何将这些人之一标记为正确答案:)
Billybonks

1
@克里斯托弗·艾伦(Christopher Allen),也许不相关,但可以完成工作: require("child_process").execSync('php -r "sleep($argv[1]);" ' + seconds);
Haitham Sweilem,2016年

节点睡眠 NPM模块可能做的伎俩(但是,我只会用它来调试)
朱利安·索罗

Answers:


211

最好的方法是将代码分成多个功能,如下所示:

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的解决方案相似,但是更加整洁并且易于扩展。


7
@LucasSeveryn,那么您做错了。这是一种核心设计模式。
Elliot Bonneville

当函数的启动不只是一个函数,而是距离需要取消同步的代码10个文件时,您该怎么办?
西里尔·杜尚-多里斯

10
@ CyrilDuchon-Doris什么?
AturSams,

3
使用@machineghost的答案获得一个优雅的基于承诺的解决方案,该解决方案无需自定义代码并支持等待/异步
Dane Macaulay

这是异步的,这意味着如果function1在3秒前终止,则它们开始相互重叠。然后,您甚至无法返回,并且几乎永远无法利用。到目前为止,我发现的唯一方法是使用debugger;
CristianTraìna,18年

426

对旧问题的新答案。今天(20171月,2019 6月)要容易得多。您可以使用async/await语法。例如:

async function init() {
  console.log(1);
  await sleep(1000);
  console.log(2);
}

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

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

2019年6月更新:通过使用最新版本的NodeJS,您可以立即使用它。无需提供命令行参数。甚至谷歌浏览器也支持它。

2020年5月更新: 很快您将可以await在异步函数之外使用语法。在此示例的顶层

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

该提案处于第3阶段。您现在可以通过使用webpack 5(alpha)来使用它,

更多信息:


5
我想您忘记了awaitsleep(1000)
Ryan Jarvis

6
这确实应该是答案,sleep node.js包可能很麻烦。
斯图尔特·艾伦

4
确实是最好的解决方案
Kostas Siabanis

40
let sleep = ms => new Promise(resolve => setTimeout(resolve, ms));对于像我这样的:) oneliner怪胎
普雷德拉格Stojadinović

21
let sleep = require('util').promisify(setTimeout);在Node 7.6+上工作并提高了可读性
BrianHVB

216

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

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

14
“复杂的高度是简单性。” -克莱尔·布斯·卢斯(Clare Booth Luce)。到目前为止,这是最好的答案,IMO。
阿诺德

3
这显然比其他答案要新得多,但它是2018年最优雅的解决方案,可以在一行中完成工作,而不会对代码产生任何其他影响。
Herick

1
这个不错。您必须使用NodeJS 7.6.0或更高版本。它不适用于较旧的版本。
瑞安·希灵顿

5
let sleep = require('util').promisify(setTimeout);是三个字符更长但可重用且更具可读性的imo
BrianHVB

2
为避免拖欠投诉,请调用resolve而不是done。即await new Promise(resolve => setTimeout(resolve, 5000))
达伦·库克

150

将延迟后要执行的代码放入setTimeout回调中:

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

2
这是非常麻烦的做法,通常是不好的做法,尤其是在OP希望在延迟之后运行程序的其余部分的情况下。看我的答案。
Elliot Bonneville

32
@ElliotBonneville这只是一个说明概念的例子。显然,您可以(应该)将代码分解为函数调用,而不是像其他任何地方一样使用内联代码。
JohnnyHK

@ChristopherKemp:事实证明Node.js有一个名为的解决方案node-fibers一探究竟。
艾略特·邦纳维尔

这是一个很好的解决方案,特别是如果您不想执行任何代码,而只想在循环中模仿“ sleep”方法。这个例子没有错。
2016年

这是延迟客户端请求的完美选择,我使用这种方法在客户端测试了我的加载微调器
moein rahimi

145

这是一种简单的阻止技术:

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

只要您的脚本中没有其他任何事情(例如回调),它就会阻塞。但是,由于这是一个控制台脚本,所以也许正是您所需要的!


27
恐怖与否,它做的很简单wait,可以阻止,并且可以用于某些测试目的。正是我要寻找的。
Clijsters

8
无需第三方库就能完美回答问题,而且很简单。然而,有人说“太可怕了” ......这是伟大如用于模拟顺便说一句重的CPU负载等非常相似,这phpied.com/sleep-in-javascript
唐·钱德尔

2
如果您尝试模拟会减慢事件循环,从而减慢其他用户/连接速度的东西,那么这也是必需的方法。添加延迟的回调不会影响其他用户/连接。
唐·奇德尔

13
这是一个spin-wait
Mike Atlas

7
@Ali似乎是目标。
费利克斯·加侬-格尼尔

63

在节点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长时间循环。我尝试了sleep软件包,但无法将其安装在Windows机器上。我最终使用:

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

要安装它,请键入:

npm install system-sleep

在您的代码中

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

奇迹般有效。


1
很好的答案,谢谢-它使一些不可能的代码成为可能-这不是一次等待
-danday74

1
在进一步研究中,该模块依赖于从另一个deasync github存储库中派生的deasync。原始存储库警告说,不要使用它,因为它是hack。它确实可以工作,但不适用于所有平台,因此,如果您需要跨平台解决方案,请避免使用此解决方案。
danday74年

1
是的,我从未体验过它的优点及其对开发人员的好处-在引擎盖下,我相信它依赖于广泛使用的C或C ++,但我猜在某些机器上它不是,那就是当它失败时,这就是导致问题的原因-如果失败,则系统睡眠会回到旋转等待状态,这将冻结代码执行
danday74 '18

1
该软件包在Mac OS上不起作用,因此不交叉兼容,因此不可行。见github.com/jochemstoel/nodejs-system-sleep/issues/4
nuzzolilo 18/12/29

1
@Nuzzolilo这很奇怪。我们在Mac OS上只有一位开发人员,他从没有提到任何错误。我怀疑这是特定的Mac OS版本。我也更新了此答案,因为睡眠基本上内置于更新版本的NodeJS中。
瑞安·希灵顿

62

使用现代Java脚本实现简单优雅的睡眠功能

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,所以这实际上不是问题。当今所有现代浏览器都支持它。

sleep对那些不熟悉的函数async/await和粗箭头运算符进行一些深入的了解,这是编写它的冗长方式:

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

但是,使用粗箭头运算符可以使其更小(更优雅)。


1
您也可以在不使用的情况下编写sleep函数async,并使其他所有内容保持不变。async尽管可能更清楚。
凯文·佩尼亚

好收获,@KevinPeña。事实上,我认为我更喜欢没有它async。根据您的建议编辑了我的答案。我认为这不会使代码更清晰。为此,我将改为使用JSDoc。
Lucio Paiva

8
我喜欢在脚本中添加以下内容:const sleep = ms => new Promise(resolve => setTimeout(resolve, ms));
korya


34

这个问题已经很老了,但是最近V8添加了可以完成OP要求的生成器。在诸如suspendgen-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');
})();

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


2
好的答案-但应该阅读yield setTimeout(suspend.resume(), 10000);
edhubbell 2015年

1
谢谢,@ edhubbell。这个答案是基于一个非常旧的suspend版本,但是您对最新版本是正确的。我将更新答案。
jmar777

这是什么版本的节点?
danday74年

1
@ danday74我无法确切记得何时未标记它们,但是从v0.12开始,它们一直位于--harmony标志后面,并且根据node.green的说法,自v4.8.4 起,它们至少没有任何标志可用:node.green/#ES2015-functions-generators-basic-functionity。但是请注意,较新的async/await语法现在为此提供了更好的解决方案,而无需使用额外的库suspend。参见以下答案以获取示例:stackoverflow.com/a/41957152/376789。从v7.10开始,异步功能可用(无标志)。
jmar777

20

在Linux / nodejs上,这对我有用:

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

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

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

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


sleep几乎是无所不在的,因为早期的Unix天公用程式角落找寻en.wikipedia.org/wiki/Sleep_%28Unix%29。窗户可能不会存在,只是“不罕见”的操作系统(无论如何您都可以尝试child_process.spawnSync('timeout', ['/T', '10'])
humanityANDpeace

17

如果您想“编码高尔夫”,则可以在此处对其他一些答案进行简化:

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

实际上,使用util.promisify不可能在不提供回调的情况下调用sleep。 nodejs.org/api/…–
Howie

1
您错过了await。它会创建一个回调,暂停操作,然后在该回调返回时重新启动代码。回调返回一个值,但是在这种情况下我们不关心它,因此的左边没有任何内容await
machineghost

1
将其写成一行以美化代码golf;)const sleep = require('util')。promisify(setTimeout);
Fabian

14

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

https://github.com/luciotato/waitfor

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

您可以在需要时按顺序进行编码,这(非常适合)可以简化您的脚本供个人使用。

使用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. 

同样,任何异步功能都可以在同步模式下调用。检查示例。


TypeError: Object #<Object> has no method 'miliseconds'
CaffeineAddiction 2014年

注释说:“ //在waitfor / paralell-tests.js中定义”从该文件中获取它。
Lucio M. Tato 2014年

2
从我那里得到它之后,wait.for/paralell-tests.js我遇到了另一个与未定义属性等有关的错误。所以我也需要复制它们。为什么不采用不需要代码的方式来组织代码?
user907860 2014年

Wait.for和其他光纤解决方案为我打开了一个全新的世界!如果可以的话,我将投票一百万次。尽管大多数nodejs社区都反对使用光纤,但我认为它们是很棒的添加,并且在回调地狱中绝对占有一席之地。
维·罗伯茨

7

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

因此,Node.js紧随其后,

process.nextTick()

以便稍后运行代码,而不是setTimeout()。例如,

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

2

有了ES6 support Promise,我们可以在没有任何第三方帮助的情况下使用它们。

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函数将成为中的resolve回调new Promise(...)

另请注意,这是异步睡眠。它不会阻止事件循环。如果需要阻止睡眠,请使用此库,该库可在C ++绑定的帮助下实现阻止睡眠。(尽管很少需要像异步环境一样在Node中阻塞睡眠。)

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


1
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从异步调用中提取数据并将解析后的数据传递回生成器的机制。有效地,它使异步调用同步。


尽管此代码可以回答问题,但提供有关如何和/或为什么解决问题的其他上下文将提高​​答案的长期价值。
唐老鸭

谢谢@DonaldDuck。我的答案中的代码可能是Java语言改进中最令人兴奋的部分。我非常惊讶,以至于一些超级聪明的人想到了解决回调地狱问题的方法。
钱陈

1

这是一个基于@ atlex2建议的基于脏阻塞方法的moment.js风格的模块。仅用于测试

const moment = require('moment');

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

module.exports = sleep;

0

如果仅出于测试目的而暂停,请尝试执行以下当前线程:

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

0

很简单,我们将等待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;
        }
    })();

0

对于某些人来说,可接受的答案无效,我找到了另一个答案,它对我也有效:如何将参数传递给setTimeout()回调?

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

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


0

与其做所有这些setTimeout,sleep和许多其他事情,不如使用它

const delay = require('delay');
await delay(2000); // will wait for 2 seconds before executing next line of code

您能解释一下“延迟”是什么,并链接到其文档吗?为什么会更好?
博西

-2

其他答案很好,但我认为我会采取不同的策略。

如果您真正想要的只是降低Linux中的特定文件速度:

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

节点myprog慢文件

每五行睡眠1秒。节点程序的运行速度与编写器一样慢。如果它正在做其他事情,它们将以正常速度继续。

mkfifo创建一个先进先出管道。这就是这项工作的原因。perl行将以您想要的速度写入。$ | = 1表示不缓冲输出。


-3

阅读完该问题的答案后,我整理了一个简单的函数,如果需要,它也可以执行回调:

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

-4

有关更多信息

yield sleep(2000); 

您应该检查Redux-Saga。但这是特定于您选择Redux作为模型框架的(尽管绝对没有必要)。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.