JavaScript中的sleep()是什么?

3710

有没有比以下的pausecomp函数(取自这里)更好的方式在JavaScript中实现sleep?

function pausecomp(millis)
{
    var date = new Date();
    var curDate = null;
    do { curDate = new Date(); }
    while(curDate-date < millis);
}

这不是JavaScript中的休眠-操作之间的延迟的重复;我需要在函数执行过程中进行真正的休眠,而不是在某段代码执行之前延迟。


273
这是一个可怕的解决方案 - 在不做任何事情的同时,你将会消耗大量的处理周期。 - 17 of 26
21
睡眠的唯一目的就是轮询或等待回调 - setInterval 和 setTimeout 都比睡眠更有效。 - annakata
65
看到人们在不理解原帖想法的情况下就拒绝了,这真是令人惊讶。有时你需要一次__真正的睡眠__。我现在需要一次真正的睡眠来测试在顶层窗口和 iframe 之间发布和接收消息时浏览器的行为。用 while 循环来保持忙碌似乎是唯一的方法。 - Devs love ZenUML
2
我想使用 sleep() 来计时动画。(我知道有更好的方法...)提问者提供的代码在 Chrome 中对我无效,因为浏览器不会在脚本中进行修改后立即更新 DOM,而是等待代码执行完成后再进行任何 DOM 更新,因此脚本会等待所有延迟的总和,然后一次性应用所有 DOM 更新。 - Ari Fordsham
16
@DevsloveZenUML,浏览器环境的设计师和开发人员为了用户的利益决定不满足你们的要求,因为在异步应用程序中赋予某人阻止整个页面的明确能力是疯狂的。 - Oleg V. Volkov
显示剩余5条评论
94个回答

19

使用 Promises 的一行代码

const wait = t => new Promise(s => setTimeout(s, t, t));

使用 Abort Signal 的 TypeScript

const wait = (x: number, signal?: AbortSignal): Promise<number> => {
  return new Promise((s, f) => {
    const id = setTimeout(s, x, x);
    signal?.addEventListener('abort', () => {
      clearTimeout(id);
      f('AbortError');
    });
  });
};

演示

const wait = t => new Promise(s => setTimeout(s, t));
// Usage
async function demo() {
    // Count down
    let i = 6;
    while (i--) {
        await wait(1000);
        console.log(i);
    }
    // Sum of numbers 0 to 5 using by delay of 1 second
    const sum = await [...Array(6).keys()].reduce(async (a, b) => {
        a = await a;
        await wait(1000);
        const result = a + b;
        console.log(`${a} + ${b} = ${result}`);
        return result;
    }, Promise.resolve(0));
    console.log("sum", sum);
}
demo();


4
抄袭/复制粘贴是真实存在的。 - T.Woody

13

为了与其他异步任务保持代码一致性,我会在一个 Promise 中封装 setTimeOut:在 Fiddle 中演示。

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

它的使用方法如下:

sleep(2000).then(function() {
   // Do something
});

如果你习惯使用Promise,那么记住语法就很容易。


4
为什么这种做法比只使用setTimeout(function(){/do something/}, 2000);更好? - JSideris

12

对于浏览器来说,我同意使用setTimeout和setInterval是正确的方法。

但是对于服务器端的代码,可能需要一个阻塞函数(例如,这样你就可以有效地进行线程同步)。

如果你正在使用Node.jsMeteor,你可能已经遇到了在纤维中使用setTimeout的限制。以下是服务器端睡眠的代码。

var Fiber = require('fibers');

function sleep(ms) {
    var fiber = Fiber.current;
    setTimeout(function() {
        fiber.run();
    }, ms);
    Fiber.yield();
}

Fiber(function() {
    console.log('wait... ' + new Date);
    sleep(1000);
    console.log('ok... ' + new Date);
}).run();
console.log('back in main');

参见:Node.js Fibers, Sleep


1
服务器可能需要阻塞函数... 我不认为强制阻塞 Node 的唯一线程并使整个服务器在几秒钟内无响应是好主意,但不管怎样。 - Jeremy Thille

12

这里的大部分回答都是误导性的,或者至少已经过时了。JavaScript没有必要是单线程的,实际上它并不是。今天所有主流的浏览器都支持workers。在此之前,其他JavaScript运行时如Rhino和Node.js支持多线程。

'JavaScript是单线程的'不是一个有效的答案。例如,在worker中运行sleep函数不会阻塞UI线程中运行的任何代码。

在支持生成器和yield的新运行时中,可以将类似于sleep函数的功能带到单线程环境中:

// This is based on the latest ES6 drafts.
// JavaScript 1.7+ (SpiderMonkey/Firefox 2+) syntax is slightly different

// Run code you want to sleep here (omit star if using JavaScript 1.7)
function* main(){
    for (var i = 0; i < 10; i++) {
        // To sleep for 10 milliseconds 10 times in a row
        yield 10;
    }

    yield 5;
    console.log('I just slept 5 milliseconds!');
}

// Resume the given generator after ms milliseconds
function resume(ms, generator){
    setTimeout(function(){
        // Omit .value if using JavaScript 1.7
        var nextSleep = generator.next().value;
        resume(nextSleep, generator);
    }, ms);
}

// Initialize a generator and get first sleep for the recursive function
var
    generator = main(),
    firstSleep = generator.next().value;

// Initialize recursive resume function
resume(firstSleep, generator);

这种睡眠的模拟与真正的睡眠函数不同,因为它不会阻止线程。它只是JavaScript当前setTimeout函数的附加功能。这种功能类型已经在Task.js中实现,应该可以在Firefox中使用。


工作器在IE中并未实现,至少到10版本为止。这目前代表了大量的用户。 - Beejor
即使如此,使用多个工作进程实现“sleep”也不切实际。如果使用Node.js,则生成器函数已经实现并可以按照描述使用。截至今天,主流浏览器并未全部实现生成器。 - Gabriel Ratener

12

这个答案适用于Node 18及更高版本!

不要再这样做了:

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

我们现在可以做到:

const { setTimeout } = require('timers/promises');
await setTimeout(3000); // sleep 3 seconds

11
自从Node.js 7.6版本以后,你可以将utils模块中的promisify函数与setTimeout结合使用。
const sleep = require('util').promisify(setTimeout)

通用使用

async function main() {
    console.time("Slept for")
    await sleep(3000)
    console.timeEnd("Slept for")
}

main()

问题使用

async function asyncGenerator() {
    while (goOn) {
      var fileList = await listFiles(nextPageToken);
      await sleep(3000)
      var parents = await requestParents(fileList);
    }
  }

11

2022更新:

只需使用此代码片段即可。

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

10
我已经在搜索/谷歌了很多关于JavaScript睡眠/等待的网页...但是如果你想让JavaScript "运行,延迟,运行",没有答案...大多数人得到的要么是"运行,运行(无用的东西),运行"或者"运行,运行+延迟运行"...
我想: 这里有一个可行的解决方案...但你必须把你的运行代码分开...: 是的,我知道,这只是一个更容易阅读的重构...仍然...
示例1:
<html>
<body>
<div id="id1">DISPLAY</div>

<script>
// JavaScript sleep by "therealdealsince1982"; copyrighted 2009
// setInterval
var i = 0;

function run() {
    // Pieces of codes to run
    if (i == 0){document.getElementById("id1").innerHTML= "<p>code segment " + i + " is ran</p>"; }
    if (i == 1){document.getElementById("id1").innerHTML= "<p>code segment " + i + " is ran</p>"; }
    if (i == 2){document.getElementById("id1").innerHTML= "<p>code segment " + i + " is ran</p>"; }
    if (i >2){document.getElementById("id1").innerHTML= "<p>code segment " + i + " is ran</p>"; }
    if (i == 5){document.getElementById("id1").innerHTML= "<p>all code segment finished running</p>"; clearInterval(t); } // End interval, stops run
    i++; // Segment of code finished running, next...
}

run();
t = setInterval("run()", 1000);

</script>
</body>
</html>

示例2:

<html>
<body>
<div id="id1">DISPLAY</div>

<script>
// JavaScript sleep by "therealdealsince1982"; copyrighted 2009
// setTimeout
var i = 0;

function run() {
    // Pieces of codes to run, can use switch statement
    if (i == 0){document.getElementById("id1").innerHTML= "<p>code segment " + i + " ran</p>"; sleep(1000);}
    if (i == 1){document.getElementById("id1").innerHTML= "<p>code segment " + i + " ran</p>"; sleep(2000);}
    if (i == 2){document.getElementById("id1").innerHTML= "<p>code segment " + i + " ran</p>"; sleep(3000);}
    if (i == 3){document.getElementById("id1").innerHTML= "<p>code segment " + i + " ran</p>";} //stops automatically
    i++;
}

function sleep(dur) {t=setTimeout("run()", dur);} // Starts flow control again after 'dur'

run(); // Starts
</script>
</body>
</html>

例子3:

<html>
<body>
<div id="id1">DISPLAY</div>

<script>
// JavaScript sleep by "therealdealsince1982"; copyrighted 2009
// setTimeout
var i = 0;

function flow() {
    run(i);
    i++; // Code segment finished running, increment i; can put elsewhere
    sleep(1000);
    if (i == 5) {clearTimeout(t);} // Stops flow, must be after sleep()
}

function run(segment) {
    // Pieces of codes to run, can use switch statement
    if (segment == 0){document.getElementById("id1").innerHTML= "<p>code segment " + segment + " is ran</p>"; }
    if (segment == 1){document.getElementById("id1").innerHTML= "<p>code segment " + segment + " is ran</p>"; }
    if (segment == 2){document.getElementById("id1").innerHTML= "<p>code segment " + segment + " is ran</p>"; }
    if (segment >2){document.getElementById("id1").innerHTML= "<p>code segment "+ segment +" is ran</p>"; }
}

function sleep(dur) {t=setTimeout("flow()", dur);} // Starts flow control again after 'dur'

flow(); // Starts flow
</script>
</body>
</html>

例子 4:

<html>
<body>
<div id="id1">DISPLAY</div>

<script>
// JavaScript sleep by "therealdealsince1982"; copyrighted 2009
// setTimeout, switch
var i = 0;

function flow() {
    switch(i)
    {
        case 0:
            run(i);
            sleep(1000);
            break;
        case 1:
            run(i);
            sleep(2000);
            break;
        case 5:
            run(i);
            clearTimeout(t); // Stops flow
            break;
        default:
            run(i);
            sleep(3000);
            break;
    }
}

function run(segment) {
    // Pieces of codes to run, can use switch statement
    if (segment == 0){document.getElementById("id1").innerHTML= "<p>code segment " + segment + " is ran</p>"; }
    if (segment == 1){document.getElementById("id1").innerHTML= "<p>code segment " + segment + " is ran</p>"; }
    if (segment == 2){document.getElementById("id1").innerHTML= "<p>code segment " + segment + " is ran</p>"; }
    if (segment >2){document.getElementById("id1").innerHTML= "<p>code segment " + segment + " is ran</p>"; }
    i++; // Current segment of code finished running, next...
}

function sleep(dur) {t=setTimeout("flow()", dur);} // Starts flow control again after 'dur'

flow(); // Starts flow control for first time...
</script>
</body>
</html>

5
好的,这可以使用setTimeout实现,但很难看出正在发生什么。使用setTimeout本身要比这容易。 - naugtur

9
如果你想要比setTimeout和setInterval更加简洁的函数,可以将它们包装在函数中,只需反转参数顺序并赋予它们良好的名称:
function after(ms, fn){ setTimeout(fn, ms); }
function every(ms, fn){ setInterval(fn, ms); }

CoffeeScript 版本:

after = (ms, fn)-> setTimeout fn, ms
every = (ms, fn)-> setInterval fn, ms

你可以使用匿名函数很好地使用它们:

after(1000, function(){
    console.log("it's been a second");
    after(1000, function(){
        console.log("it's been another second");
    });
});

现在它很容易阅读为“N毫秒后...”(或“每N毫秒...”)

9
我建议这种方法适用于之前从事Python开发的人。
const sleep = (time) => {
   return new Promise((resolve) => setTimeout(resolve, Math.ceil(time * 1000)));
};

使用方法:

await sleep(10) // for 10 seconds

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