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

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

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


当前回答

JavaScript函数不允许任何暂停。使用同步JavaScript,可以实现过程。过程等待I/O操作和睡眠超时。它可用于JavaScript 1.7。

演示:

演示睡眠演示可暂停程序

其他回答

(见2016年更新答案)

我认为想要执行一个动作,等待,然后执行另一个动作是完全合理的。如果您习惯于用多线程语言编写,那么您可能会有一个想法,即在线程唤醒之前,在一定的时间内执行。

这里的问题是JavaScript是一个基于单线程事件的模型。虽然在特定情况下,让整个发动机等待几秒钟可能会很好,但一般来说这是不好的做法。假设我想在编写自己的函数时使用您的函数?当我调用你的方法时,我的方法都会冻结。如果JavaScript能够以某种方式保存函数的执行上下文,将其存储在某个地方,然后将其带回并稍后继续,那么睡眠可能会发生,但这基本上就是线程。

因此,你基本上坚持了别人的建议——你需要将代码分解为多个函数。

那么,你的问题有点错误。没有办法按照你想要的方式睡觉,你也不应该追求你建议的解决方案。

或者只创建一个:

function yourFunction(){

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

function myFunc(){
   return;
}

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

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

每个人都熟悉传统的编程风格,即以同步的方式逐行运行代码。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,所以如果在严格模式下运行可能会有麻烦。

如果(像我一样)你在Rhino中使用JavaScript,你可以使用。。。

try
{
  java.lang.Thread.sleep(timeInMilliseconds);
}
catch (e)
{
  /*
   * This will happen if the sleep is woken up - you might want to check
   * if enough time has passed and sleep again if not - depending on how
   * important the sleep time is to you.
   */
}

如果你喜欢建议,不要失去表现。setTimeout是您的预期睡眠。然而,如果您需要一种语法,其中代码被睡眠“中间分割”,我们可以这样做:

sleep = function(tm, fn){
   window.setTimeout(fn, tm);
}

然后,准备如下功能:

var fnBeforeSleep = function(){

  // All code before sleep

}

var fnAfterSleep = function(){

  // All code after sleep

}

然后:

fnBeforeSleep();
sleep(2000, fnAfterSleep);

// Yep! Syntactically, it is very close to:

fnBeforeSleep();
sleep(2000);
fnAfterSleep();