有没有比下面的pausecomp函数(取自此处)更好的方法来设计JavaScript中的睡眠?

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

这不是JavaScript中的Sleep的重复-动作之间的延迟;我希望在函数的中间有一个真正的睡眠,而不是在代码执行之前有一段延迟。


当前回答

我浏览了一天的解决方案,但我仍在思考如何在使用回调时保持可链接性。

每个人都熟悉传统的编程风格,即以同步的方式逐行运行代码。SetTimeout使用回调,因此下一行不会等待它完成。这让我思考如何使其“同步”,从而实现“睡眠”功能。

从一个简单的协同程序开始:

function coroutine() {
    console.log('coroutine-1:start');
    sleepFor(3000); // Sleep for 3 seconds here
    console.log('coroutine-2:complete');
}

我想中间睡3秒钟,但我不想控制整个流程,所以协同程序必须由另一个线程执行。我考虑Unity Yield Instruction,并按以下方式修改协程:

function coroutine1() {
    this.a = 100;
    console.log('coroutine1-1:start');
    return sleepFor(3000).yield; // Sleep for 3 seconds here
    console.log('coroutine1-2:complete');
    this.a++;
}

var c1 = new coroutine1();

声明sleepFor原型:

sleepFor = function(ms) {
    var caller = arguments.callee.caller.toString();
    var funcArgs = /\(([\s\S]*?)\)/gi.exec(caller)[1];
    var args = arguments.callee.caller.arguments;
    var funcBody = caller.replace(/^[\s\S]*?sleepFor[\s\S]*?yield;|}[\s;]*$/g,'');
    var context = this;
    setTimeout(function() {
        new Function(funcArgs, funcBody).apply(context, args);
    }, ms);
    return this;
}

运行协同程序1(我在InternetExplorer11和Chrome49中进行了测试)后,您将看到它在两个控制台语句之间休眠3秒。它使代码与传统样式一样漂亮。

棘手的一点是在睡眠中。它将调用者函数体作为字符串读取,并将其分成两部分。拆下上部并通过下部创建另一个功能。等待指定的毫秒数后,它通过应用原始上下文和参数来调用创建的函数。对于原始流,它将像往常一样以“返回”结束。为了“收益”?它用于正则表达式匹配。这是必要的,但毫无用处。

它根本不是100%完美,但它至少实现了我的工作。我不得不提到使用这段代码时的一些限制。当代码被分成两部分时,“return”语句必须在外部,而不是在任何循环或{}中。即

function coroutine3() {
    this.a = 100;
    console.log('coroutine3-1:start');
    if(true) {
        return sleepFor(3000).yield;
    } // <- Raise an exception here
    console.log('coroutine3-2:complete');
    this.a++;
}

上述代码一定有问题,因为所创建的函数中不能单独存在右括号。另一个限制是“var xxx=123”声明的所有局部变量都不能传递到下一个函数。您必须使用“this.xxx=123”来实现相同的目标。如果您的函数有参数,并且它们发生了更改,则修改后的值也无法传递到下一个函数。

function coroutine4(x) { // Assume x=abc
    var z = x;
    x = 'def';
    console.log('coroutine4-1:start' + z + x); // z=abc, x=def
    return sleepFor(3000).yield;
    console.log('coroutine4-2:' + z + x); // z=undefined, x=abc
}

我将介绍另一个函数原型:waitFor

waitFor = function(check, ms) {
    var caller = arguments.callee.caller.toString();
    var funcArgs = /\(([\s\S]*?)\)/gi.exec(caller)[1];
    var args = arguments.callee.caller.arguments;
    var funcBody = caller.replace(/^[\s\S]*?waitFor[\s\S]*?yield;|}[\s;]*$/g,'');
    var context = this;
    var thread = setInterval(function() {
        if(check()) {
            clearInterval(thread);
            new Function(funcArgs, funcBody).apply(context, args);
        }
    }, ms?ms:100);
    return this;
}

它等待“check”函数,直到它返回true。它每100毫秒检查一次值。您可以通过传递额外的参数来调整它。考虑测试协程2:

function coroutine2(c) {
    /* Some code here */
    this.a = 1;
    console.log('coroutine2-1:' + this.a++);
    return sleepFor(500).yield;

    /* Next */
    console.log('coroutine2-2:' + this.a++);
    console.log('coroutine2-2:waitFor c.a>100:' + c.a);
    return waitFor(function() {
        return c.a>100;
    }).yield;

    /* The rest of the code */
    console.log('coroutine2-3:' + this.a++);
}

也是我们迄今为止喜爱的漂亮款式。实际上,我讨厌嵌套回调。很容易理解,协程2将等待协程1的完成。有趣的好的,然后运行以下代码:

this.a = 10;
console.log('outer-1:' + this.a++);
var c1 = new coroutine1();
var c2 = new coroutine2(c1);
console.log('outer-2:' + this.a++);

输出为:

outer-1:10
coroutine1-1:start
coroutine2-1:1
outer-2:11
coroutine2-2:2
coroutine2-2:waitFor c.a>100:100
coroutine1-2:complete
coroutine2-3:3

在初始化协程1和协程2后,立即完成外部。然后,协程1将等待3000毫秒。等待500毫秒后,子程序2将进入步骤2。之后,一旦检测到协程1.a值>100,它将继续执行步骤3。

请注意,有三种上下文可以保存变量“a”。一个是外部,值为10和11。另一个在协程1中,其值为100和101。最后一个在协程2中,其值为1、2和3。在协程2中,它还等待来自协程1的c.a,直到其值大于100。3个上下文是独立的。

复制和粘贴的完整代码:

sleepFor = function(ms) {
    var caller = arguments.callee.caller.toString();
    var funcArgs = /\(([\s\S]*?)\)/gi.exec(caller)[1];
    var args = arguments.callee.caller.arguments;
    var funcBody = caller.replace(/^[\s\S]*?sleepFor[\s\S]*?yield;|}[\s;]*$/g,'');
    var context = this;
    setTimeout(function() {
        new Function(funcArgs, funcBody).apply(context, args);
    }, ms);
    return this;
}

waitFor = function(check, ms) {
    var caller = arguments.callee.caller.toString();
    var funcArgs = /\(([\s\S]*?)\)/gi.exec(caller)[1];
    var args = arguments.callee.caller.arguments;
    var funcBody = caller.replace(/^[\s\S]*?waitFor[\s\S]*?yield;|}[\s;]*$/g,'');
    var context = this;
    var thread = setInterval(function() {
        if(check()) {
            clearInterval(thread);
            new Function(funcArgs, funcBody).apply(context, args);
        }
    }, ms?ms:100);
    return this;
}

function coroutine1() {
    this.a = 100;
    console.log('coroutine1-1:start');
    return sleepFor(3000).yield;
    console.log('coroutine1-2:complete');
    this.a++;
}

function coroutine2(c) {
    /* Some code here */
    this.a = 1;
    console.log('coroutine2-1:' + this.a++);
    return sleepFor(500).yield;

    /* next */
    console.log('coroutine2-2:' + this.a++);
    console.log('coroutine2-2:waitFor c.a>100:' + c.a);
    return waitFor(function() {
        return c.a>100;
    }).yield;

    /* The rest of the code */
    console.log('coroutine2-3:' + this.a++);
}

this.a = 10;
console.log('outer-1:' + this.a++);
var c1 = new coroutine1();
var c2 = new coroutine2(c1);
console.log('outer-2:' + this.a++);

它在Internet Explorer 11和Chrome 49中进行了测试。因为它使用arguments.callee,所以如果在严格模式下运行可能会有麻烦。

其他回答

你不能在JavaScript中做这样的睡眠,或者更确切地说,你不应该这样做。运行sleep或while循环将导致用户的浏览器挂起,直到循环完成。

使用您引用的链接中指定的计时器。

使用更好的DX实现更安全的异步睡眠

我使用睡眠进行调试。。。我忘了用wait太多次了。让我挠头。我在写下面的实时片段时忘记使用await。。。不再!

如果您在1毫秒内运行了两次,下面的睡眠功能会提醒您。如果您确定使用了await,它还支持传递一个相当的参数。它不会投掷,因此可以安全地用作睡眠的替代品。享受

是的,实时片段中有一个JavaScript版本。

// Sleep, with protection against accidentally forgetting to use await
export const sleep = (s: number, quiet?: boolean) => {
  const sleepId = 'SLEEP_' + Date.now()
  const G = globalThis as any
  if (G[sleepId] === true && !quiet) {
    console.error('Did you call sleep without await? use quiet to hide msg.')
  } else {
    G[sleepId] = true
  }
  return new Promise((resolve) => {
    !quiet && setTimeout(() => {
      delete G[sleepId]
    }, 1)
    setTimeout(resolve, (s * 1000) | 0)
  })
}

//睡眠,防止意外忘记使用等待常量睡眠=(s,安静)=>{constsleepId='SLEEP_'+Date.now()const G=全局此if(G[sleepId]==true&&!quiet){console.error('您是否在没有等待的情况下调用sleep?使用quiet隐藏消息。')}其他{G[sleepId]=真}return new Promise((resolve)=>{!quiet&&setTimeout(()=>{删除G[sleepId]}, 1)setTimeout(解析,(s*1000)|0)})}常量main=async()=>{console.log('休眠1秒…')等待睡眠(1)console.log('使用等待的目标…')睡眠(99)睡眠(99)等待睡眠(1,true)console.log(“开发人员快乐!”)}main()

与公认更容易阅读的相比:

const sleep = (s: number) =>
  new Promise((p) => setTimeout(p, (s * 1000) | 0))

第一:

定义要执行的函数,如下所示:

function alertWorld(){
  alert("Hello, World!");
}

然后使用setTimeout方法安排其执行:

setTimeout(alertWorld, 1000)

注意两件事

第二个参数是以毫秒为单位的时间作为第一个参数,必须只传递函数的名称(引用),而不传递括号

这里的大多数答案都是错误的,或者至少是过时的。没有理由JavaScript必须是单线程的,事实上也不是。今天,所有主流浏览器都支持工人。在此之前,Rhino和Node.js等其他JavaScript运行时支持多线程。

“JavaScript是单线程的”不是有效答案。例如,在工作线程中运行睡眠函数不会阻止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中使用。

或者只创建一个:

function yourFunction(){

   // Do something
   setInterval(myFunc(), 1000);
   // Do something else
}

function myFunc(){
   return;
}

这将只等待指定的时间间隔并调用函数,而该函数将不执行任何操作。