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

740

我正在为个人需求开发控制台脚本。我需要能够暂停一段较长的时间,但是从我的研究来看,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无法识别这个。

我该如何实现这个延长的暂停时间呢?


4
@Christopher Allen,也许不是很相关,但可以胜任任务: require("child_process").execSync('php -r "sleep($argv[1]);" ' + seconds); 意思是通过Node.js的child_process模块执行一条命令,即在PHP中运行一个代码段来使程序等待指定的秒数。 - Haitham Sweilem
node-sleep npm 模块可能会有用(但是,我只会在调试时使用它)。 - julian soro
1
这回答解决了你的问题吗?JavaScript 中类似于 sleep() 函数的方法是什么? - Dan Dascalescu
2
请不要编写自己的 Promises!使用 import { setTimeout } from 'timers/promises' - Nick Grealy
27个回答

922

2021年1月更新:你甚至可以在Node 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月)要容易得多。你可以使用新的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,您必须使用node-v7或node-v8,并使用--harmony标志。

2019年6月更新:通过使用最新版本的NodeJS,您可以直接使用它,无需提供命令行参数。即使是Google Chrome也支持它。

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

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

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

在此之前,您可以将脚本包装在一个自调用的异步函数中:

(async function() {
const sleep = ms => new Promise(resolve => setTimeout(resolve, ms))
console.log(1)
await sleep(1000)
console.log(2)
})()

更多信息:


5
我认为你忘记在 sleep(1000) 前面加上 await 关键字了。 - Ryan Jarvis
7
这真的应该是答案,sleep Node.js 包可能会引起麻烦。 - Stuart Allen
67
让睡眠等于ms => new Promise(resolve => setTimeout(resolve, ms)); 对于像我这样的单行代码迷 :) - Predrag Stojadinović
29
let sleep = require('util').promisify(setTimeout); 可在 Node 7.6+ 版本中使用,并且可以提高代码的可读性。 - Brian H.
2
这是一个很好的答案,因为它总结了多年来完成此操作的不同方法。请考虑在顶部使用 timers/promises 中的 setTimeout 进行另一次更新。请查看 @maxim-orlov 的答案,该答案远低于 (链接)。 - pm_
显示剩余3条评论

733

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

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

73
"高超的精致在于简约。" -- 克莱尔·布斯·卢斯。 - arnold
3
这显然比其他答案要新,但截至2018年,它是最优雅的解决方案,而且只需一行代码即可完成任务,不会对代码产生任何其他影响。 - Herick
1
这是个不错的东西。但是你必须使用 NodeJS 7.6.0 或以上的版本。它在旧版本上无法工作。 - Ryan Shillington
10
在我看来,“let sleep = require('util').promisify(setTimeout);”虽然比较长,但是更易于重复使用和阅读。 - Brian H.
2
为避免 eslint 的警告,请将其称为 resolve 而不是 done。即 await new Promise(resolve => setTimeout(resolve, 5000)) - Darren Cook
显示剩余7条评论

250

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

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


8
你做错了什么,这是一个核心的设计模式。 - Elliot Bonneville
当函数的开头不仅距离需要解除异步化的代码有10个文件之遥,你要怎么办呢? - Cyril Duchon-Doris
22
@CyrilDuchon-Doris 什么? - AturSams
3
使用 @machineghost 的回答来获得一个优雅的基于 Promise 的解决方案,它不需要自定义代码,并支持 await/async。 - Dane Macaulay
这是异步的,这意味着如果function1在3秒之前终止,它们会开始重叠。然后你甚至不能返回,几乎从来没有可用性。到目前为止我找到的唯一方法是使用debugger; - Christian Vincenzo Traina

199

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

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

就阻止你的脚本中发生任何其他事情(例如回调函数),因此它在某种程度上是“blocking”(阻塞)的。但由于这是一个控制台脚本,也许这正是你所需要的!


41
无论好坏,它执行简单的“等待”操作,会阻塞并且适用于某些测试目的。这正是我一直在寻找的。 - Clijsters
14
完美地回答了问题,不需要使用第三方库,并且简单易懂。然而有些人说它很糟糕... 这对于模拟大量 CPU 负载等操作非常有用。顺便提一下,这与 https://www.phpied.com/sleep-in-javascript/ 非常相似。 - Don Cheadle
26
这是一个“自旋等待”。 - Mike Atlas
12
@Ali 看起来那似乎是目标。 - Félix Adriyel Gagnon-Grenier
7
这是针对这个问题唯一正确的答案。OP明确要求调用等待函数、从函数返回并继续执行。(不使用回调函数,不使用异步操作,不需要重写整个调用堆栈来处理承诺。)唯一可以完全满足OP需求的其他解决方案是调用外部程序,例如Windows上的"SLEEP.EXE"。 - blitter
显示剩余10条评论

164

将您想要延迟执行的代码放在setTimeout回调函数中:

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

5
这样做非常混乱而且通常是不良的实践,特别是如果 OP 希望在延迟后程序的其余部分继续运行。请查看我的答案。 - Elliot Bonneville
38
这只是一个例子,用来说明概念。显然,你可以(应该)将代码重构成函数调用,而不是使用内联代码,就像任何其他地方一样。 - JohnnyHK
@ChristopherKemp:原来Node.js有一个叫做node-fibers的解决方案。看看吧。 - Elliot Bonneville
这是一个很好的解决方案,特别是如果您不想执行任何代码,只想在循环内模拟“sleep”方法。这个例子没有任何问题。 - Halfstop
这非常适合延迟来自客户端的请求,我使用这种方法来测试客户端加载旋转器。 - moein rahimi

122

在 Node 7.6.0 或更高版本上

Node 原生支持等待:

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中(原回答)

我想要一个异步的sleep函数,它可以在Windows和Linux上工作,并且不会通过长时间的while循环占用我的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

运行得非常好。


3
非常感谢您的出色回答,它使一些不可能的代码变得可行,这并不是一个自旋等待。 - danday74
1
进一步研究发现该模块依赖于deasync,它是从另一个deasync github仓库中分叉出来的。原始仓库警告不要使用它,因为它是一个hack。它确实能工作,但并非在所有平台上都适用,因此如果您需要跨平台解决方案,请避免使用此模块。 - danday74
2
这个软件包在Mac OS上无法工作,因此不具备跨平台兼容性,也因此无法使用。请参见https://github.com/jochemstoel/nodejs-system-sleep/issues/4 - marknuzz
1
@Nuzzolilo 这很奇怪。我们有一个Mac OS上的开发人员,他从来没有提到过任何问题。我怀疑这是特定的Mac OS版本。我还更新了这个答案,因为睡眠基本上已经内置在新版本的NodeJS中。 - Ryan Shillington
2
这个应该更高。非常棒的答案。 - dz210
显示剩余8条评论

70

使用现代JavaScript实现简单优雅的睡眠函数

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

没有依赖关系,没有回调地狱;就是这样 :-)


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

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

main();
“drawback”的缺点是你的主函数现在也必须是async。但是,考虑到您已经在编写现代JavaScript代码,您很可能(或者至少应该!)在代码中到处使用async/await,所以这实际上并不是问题。所有现代浏览器今天都支持它。
针对那些不习惯使用async/await和箭头函数操作符的人,以下是撰写“休眠”功能的冗长方式的一些见解:
function sleep(millis) {
    return new Promise(function (resolve, reject) {
        setTimeout(function () { resolve(); }, millis);
    });
}

然而,使用箭头函数会使其更加简洁(并且更加优雅)。


1
你也可以不使用 asyncsleep 函数,保持其他一切不变。但是使用 async 可能更清晰明了。 - Kevin Peña
不错的发现,@KevinPeña。事实上,我认为我更喜欢没有“async”。使用你的建议编辑了我的答案。我不认为它会使代码更清晰;为此,我会转而使用JSDoc。 - Lucio Paiva
9
我喜欢在我的脚本中加入以下一行代码: const sleep = ms => new Promise(resolve => setTimeout(resolve, ms)); 该代码的作用是暂停程序执行一段时间,等待指定的毫秒数后再继续执行。 - korya

59
从Node.js 15及更高版本开始,您可以使用Timers Promises API。您不再需要将setTimeout转换为Promise或依赖于第三方库。
import { setTimeout } from 'node:timers/promises';

await setTimeout(1000);

3
这是正确的解决方案 - 请不要编写自己的承诺。 - Nick Grealy
1
绝对是正确的解决方案。 - undefined

53

5
MacOS上运行正常,但在CentOS上由于node_gyp错误遇到了问题,似乎不可移植。 - AechoLiu
2
可能问题不是由操作系统引起的,而是由节点构建引起的。 - Urgotto
这是一个较新的功能,因此需要较新的Node版本。 - machineghost

43

如果您想要进行“代码高尔夫”,您可以制作其他答案的较短版本:

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

但是我个人认为,真正理想的答案是使用Node的util库和其promisify函数,该函数专门用于这种情况(将之前基于非Promise的东西转换成基于Promise的版本):

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

无论哪种情况,您都可以使用await调用您的sleep函数来暂停:

返回结果:

无论哪种情况,您都可以使用await调用您的sleep函数来暂停:

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

编辑:如评论中所述,你甚至可以将其缩减为一行:

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

或者,如果你甚至不想麻烦去创建一个sleep函数:

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

实际上,使用util.promisify无法调用sleep而不提供回调函数。https://nodejs.org/api/timers.html#timers_settimeout_callback_delay_args - Howie
1
你缺少了 await。它会创建一个回调函数,暂停代码执行,等待回调返回后重新启动代码。虽然回调函数会返回一个值,但在这种情况下我们不关心它,所以在 await 左侧没有任何内容。 - machineghost
1
const sleep = require('util').promisify(setTimeout); - Fabian
2
@Fabian,如果你只需要一次,可以使用await require('util').promisify(setTimeout)(1000); - Ed'ka

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接