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

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

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


当前回答

从该链接获取的代码不会冻结计算机。但它只在Firefox中有效。

/**
 * Netscape compatible WaitForDelay function.
 * You can use it as an alternative to Thread.Sleep() in any major programming language
 * that support it while JavaScript it self doesn't have any built-in function to do such a thing.
 * parameters:
 * (Number) delay in millisecond
 */
function nsWaitForDelay(delay) {
    /**
     * Just uncomment this code if you're building an extension for Firefox.
     * Since Firefox 3, we'll have to ask for user permission to execute XPCOM objects.
     */
    netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");

    // Get the current thread.
    var thread = Components.classes["@mozilla.org/thread-manager;1"].getService(Components.interfaces.nsIThreadManager).currentThread;

    // Create an inner property to be used later as a notifier.
    this.delayed = true;

    /* Call JavaScript setTimeout function
      * to execute this.delayed = false
      * after it finishes.
      */
    setTimeout("this.delayed = false;", delay);

    /**
     * Keep looping until this.delayed = false
     */
    while (this.delayed) {
        /**
         * This code will not freeze your browser as it's documented in here:
         * https://developer.mozilla.org/en/Code_snippets/Threads#Waiting_for_a_background_task_to_complete
         */
        thread.processNextEvent(true);
    }
}

其他回答

我相信有一百万种方法可以做得更好,但我想我可以通过创建一个对象来尝试一下:

// execute code consecutively with delays (blocking/non-blocking internally)
function timed_functions()
{
    this.myfuncs = [];
    this.myfuncs_delays = []; // mirrors keys of myfuncs -- values stored are custom delays, or -1 for use default
    this.myfuncs_count = 0; // increment by 1 whenever we add a function
    this.myfuncs_prev    = -1; // previous index in array
    this.myfuncs_cur    = 0; // current index in array
    this.myfuncs_next  = 0; // next index in array
    this.delay_cur     = 0; // current delay in ms
    this.delay_default = 0; // default delay in ms
    this.loop = false;      // will this object continue to execute when at end of myfuncs array?
    this.finished = false;  // are we there yet?
    this.blocking = true;   // wait till code completes before firing timer?
    this.destroy = false;   // <advanced> destroy self when finished


    this.next_cycle = function() {
        var that  = this;
        var mytimer = this.delay_default;

        if(this.myfuncs_cur > -1)
            if(this.myfuncs_delays[this.myfuncs_cur] > -1)
                mytimer = this.myfuncs_delays[this.myfuncs_cur];

        console.log("fnc:" + this.myfuncs_cur);
        console.log("timer:" + mytimer);
        console.log("custom delay:" + this.myfuncs_delays[this.myfuncs_cur]);
        setTimeout(function() {
        // Time is up! Next cycle...
        that.cycle();

    }, mytimer);
}

this.cycle = function() {

    // Now check how far we are along our queue.. is this the last function?
    if(this.myfuncs_next + 1 > this.myfuncs_count)
    {
        if(this.loop)
        {
            console.log('looping..');
            this.myfuncs_next = 0;
        }
        else
            this.finished = true;
    }

    // First check if object isn't finished
    if(this.finished)
        return false;

    // HANDLE NON BLOCKING //
    if(this.blocking != true) // Blocking disabled
    {
        console.log("NOT BLOCKING");
        this.next_cycle();
    }

    // Set prev = current, and current to next, and next to new next
    this.myfuncs_prev = this.myfuncs_cur;
    this.myfuncs_cur  = this.myfuncs_next;
    this.myfuncs_next++;

    // Execute current slot
    this.myfuncs[this.myfuncs_cur]();

    // HANDLE BLOCKING
    if(this.blocking == true)  // Blocking enabled
    {
        console.log("BLOCKING");
        this.next_cycle();
    }

    return true;
};

// Adders
this.add = {
    that:this,

    fnc: function(aFunction) {
        // Add to the function array
        var cur_key = this.that.myfuncs_count++;
        this.that.myfuncs[cur_key] = aFunction;
        // Add to the delay reference array
        this.that.myfuncs_delays[cur_key] = -1;
    }
}; // end::this.add

// setters
this.set = {
    that:this,

    delay: function(ms) {
        var cur_key = this.that.myfuncs_count - 1;
        // This will handle the custom delay array this.that.myfunc_delays
        // Add a custom delay to your function container

        console.log("setting custom delay. key: "+ cur_key + " msecs: " + ms);
        if(cur_key > -1)
        {
            this.that.myfuncs_delays[cur_key] = ms;
        }
        // So now we create an entry on the delay variable
    },

    delay_cur:      function(ms) { this.that.delay_cur = ms; },
    delay_default:  function(ms) { this.that.delay_default = ms; },
    loop_on:        function()   { this.that.loop = true; },
    loop_off:       function()   { this.that.loop = false; },
    blocking_on:    function()   { this.that.blocking = true; },
    blocking_off:   function()   { this.that.blocking = false; },

    finished:            function(aBool) { this.that.finished = true; }
}; // end::this.set


// Setters
this.get = {
    that:this,

    delay_default: function() { return this.that.delay_default; },
    delay_cur:     function() { return this.that.delay_cur; }
    }; // end::this.get

} // end:::function timed_functions()

使用方式如下:

// // // BEGIN :: TEST // // //

// Initialize
var fncTimer = new timed_functions;

// Set some defaults
fncTimer.set.delay_default(1000);
fncTimer.set.blocking_on();
// fncTimer.set.loop_on();
// fncTimer.set.loop_off();


// BEGIN :: ADD FUNCTIONS (they will fire off in order)
fncTimer.add.fnc(function() {
    console.log('plan a (2 secs)');
});
fncTimer.set.delay(2000); // Set a custom delay for previously added function

fncTimer.add.fnc(function() {
    console.log('hello world (delay 3 seconds)');
});
fncTimer.set.delay(3000);

fncTimer.add.fnc(function() {
    console.log('wait 4 seconds...');
});
fncTimer.set.delay(4000);

fncTimer.add.fnc(function() {
    console.log('wait 2 seconds');
});
fncTimer.set.delay(2000);

fncTimer.add.fnc(function() {
    console.log('finished.');
});
// END :: ADD FUNCTIONS


// NOW RUN
fncTimer.cycle(); // Begin execution


// // // END :: TEST // // //

我认为这个问题很好,指出了重要的观点和考虑因素。

话虽如此,我认为问题的核心在于意图和理解开发人员(您)想要控制什么。

首先,名称sleep是一个重载的命名选择。即,“什么”将被“睡眠”;作为一名开发人员,我控制着什么?

在任何语言引擎中,在任何OS进程上运行,在任何裸机或托管系统上,“开发人员”都不控制(拥有)OS共享资源CPU内核(和/或线程),除非他们正在编写OS/process系统本身。CPU是一种时间共享资源,工作执行进度的货币是分配给系统上要执行的所有工作的“周期”。

作为一名应用程序/服务开发人员,最好考虑我是由操作系统进程/语言引擎管理的工作流活动流的控制者。在一些系统上,这意味着我控制一个本机os线程(可能共享CPU内核),在其他系统上,意味着我可以控制一个异步延续工作流链/树。

对于JavaScript,它是“后者”。

因此,当需要“睡眠”时,我打算让我的工作流在执行过程中“延迟”一段时间,然后再继续执行工作流中的下一个“步骤”(阶段/活动/任务)。

这是“恰当”的说法,即作为一个开发人员,最容易将模型作为线性代码流来工作;根据需要采用工作流的组合来进行缩放。

今天,在JavaScript中,我们可以选择使用高效的多任务20世纪80年代基于角色的延续架构(重新标记为现代Futures/Promises/then/await等)来设计这样的线性工作流。

考虑到这一点,我的答案不是提供新的技术解决方案,而是关注问题本身的意图和设计视角。

我建议,任何答案都要从思考上述概念开始,然后选择一个提醒和暗示意图的名字(而不是睡眠)。

工作流选择1:delayWorkForMs(nMsToDelay)选项2:delaySyncSequenceForMs(msPeriod)

async delayAsyncSequenceForMs(msPeriod) {
  await new Promise(resolve => setTimeout(resolve, msPeriod));
}

请记住,任何异步函数都会返回Promise,await只能在异步函数中使用。(哈哈,你可能会问自己为什么……)。注意事项1:不要使用“循环”来消耗cpu周期。注意事项2:在JavaScript模型中,当在非异步函数中时,您不能“延迟”(等待)“异步”工作流的执行(除非您正在做不必要的坏事,而不需要占用cpu周期)。您只能在“异步”函数中“延迟”代码步骤。在内部,“异步”函数被建模为每个await关键字处的入口点/延续的集合。如果您熟悉倒勾插值模型,您可以“将await”视为在概念上建模,类似于编写倒勾字符串,如:

  // Conceptualizing, using an interpolation example to illustrate
  // how to think about "await" and "async" functions
  `code${await then-restart-point}more-code${await then-restart-point}`

(见2016年更新答案)

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

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

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

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

在某些情况下,一个很好的选择是显示一个顶级消息面板来停止用户交互,然后在得到等待的结果时再次隐藏它(异步)。这允许浏览器继续执行后台任务,但会暂停工作流,直到您返回结果。

第一:

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

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

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

setTimeout(alertWorld, 1000)

注意两件事

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