代码之家  ›  专栏  ›  技术社区  ›  Just Jake

在setTimeout()中查找剩余时间?

  •  78
  • Just Jake  · 技术社区  · 16 年前

    我正在写一些JavaScript,它与我不拥有的、无法(合理地)更改的库代码进行交互。它创建JavaScript超时,用于在一系列有时间限制的问题中显示下一个问题。这不是真正的代码,因为它被混淆得无以复加。图书馆的工作如下:

    ....
    // setup a timeout to go to the next question based on user-supplied time
    var t = questionTime * 1000
    test.currentTimeout = setTimeout( showNextQuestion(questions[i+1]), t );
    

    我想在屏幕上放一个进度条 questionTime * 1000 通过询问由创建的计时器 setTimeout . 唯一的问题是,似乎没有办法做到这一点。有没有 getTimeout 我丢失的功能?我能找到的有关javascript超时的唯一信息仅与通过 setTimeout( function, time) 和删除通过 clearTimeout( id ) .

    我正在寻找一个函数,它返回超时触发前的剩余时间,或调用超时后经过的时间。我的进度条形码如下:

    var  timeleft = getTimeout( test.currentTimeout ); // I don't know how to do this
    var  $bar = $('.control .bar');
    while ( timeleft > 1 ) {
        $bar.width(timeleft / test.defaultQuestionTime * 1000);
    }
    

    DR: 如何查找javascript setTimeout()之前的剩余时间?


    这是我现在使用的解决方案。我查看了负责测试的库部分,并对代码进行了解读(很糟糕,违反了我的许可)。

    // setup a timeout to go to the next question based on user-supplied time
    var t = questionTime * 1000
    test.currentTimeout = mySetTimeout( showNextQuestion(questions[i+1]), t );
    

    我的代码是:

    // wrapper for setTimeout
    function mySetTimeout( func, timeout ) {
        timeouts[ n = setTimeout( func, timeout ) ] = {
            start: new Date().getTime(),
            end: new Date().getTime() + timeout
            t: timeout
        }
        return n;
    }

    这在任何不是IE 6的浏览器中都非常适用。甚至是最初的iPhone,在那里我希望事情变得异步。

    12 回复  |  直到 7 年前
        1
  •  25
  •   lawnsea    16 年前

    如果无法修改库代码,则需要重新定义setTimeout以满足您的目的。下面是您可以做的一个示例:

    (function () {
    var nativeSetTimeout = window.setTimeout;
    
    window.bindTimeout = function (listener, interval) {
        function setTimeout(code, delay) {
            var elapsed = 0,
                h;
    
            h = window.setInterval(function () {
                    elapsed += interval;
                    if (elapsed < delay) {
                        listener(delay - elapsed);
                    } else {
                        window.clearInterval(h);
                    }
                }, interval);
            return nativeSetTimeout(code, delay);
        }
    
        window.setTimeout = setTimeout;
        setTimeout._native = nativeSetTimeout;
    };
    }());
    window.bindTimeout(function (t) {console.log(t + "ms remaining");}, 100);
    window.setTimeout(function () {console.log("All done.");}, 1000);
    

    这不是生产代码,但应该让您走上正确的道路。注意,每次超时只能绑定一个侦听器。我没有用这个做过广泛的测试,但是它在Firebug中有效。

    一个更健壮的解决方案将使用包装setTimeout的相同技术,但是使用从返回的timeoutid到监听器的映射来处理每个超时的多个监听器。您还可以考虑包装ClearTimeout,以便在超时被清除时分离侦听器。

        2
  •  49
  •   Fluffy    12 年前

    对于记录,有一种方法可以在node.js中获取剩余时间:

    var timeout = setTimeout(function() {}, 3600 * 1000);
    
    setInterval(function() {
        console.log('Time left: '+getTimeLeft(timeout)+'s');
    }, 2000);
    
    function getTimeLeft(timeout) {
        return Math.ceil((timeout._idleStart + timeout._idleTimeout - Date.now()) / 1000);
    }
    

    印刷品:

    $ node test.js 
    Time left: 3599s
    Time left: 3597s
    Time left: 3595s
    Time left: 3593s
    

    这在firefox中似乎不起作用,但由于node.js是javascript,我认为这句话可能对寻找节点解决方案的人有所帮助。

        3
  •  45
  •   Community Mohan Dere    9 年前

    编辑:我真的认为我做的更好: https://stackoverflow.com/a/36389263/2378102

    我编写了这个函数,并经常使用它:

    function timer(callback, delay) {
        var id, started, remaining = delay, running
    
        this.start = function() {
            running = true
            started = new Date()
            id = setTimeout(callback, remaining)
        }
    
        this.pause = function() {
            running = false
            clearTimeout(id)
            remaining -= new Date() - started
        }
    
        this.getTimeLeft = function() {
            if (running) {
                this.pause()
                this.start()
            }
    
            return remaining
        }
    
        this.getStateRunning = function() {
            return running
        }
    
        this.start()
    }
    

    制作计时器:

    a = new timer(function() {
        // What ever
    }, 3000)
    

    因此,如果您想要剩余时间,只需执行以下操作:

    a.getTimeLeft()
    
        4
  •  3
  •   vol7ron    12 年前

    Javascript的事件堆栈无法按照您的想法进行操作。

    创建超时事件时,会将其添加到事件队列中,但在激发该事件时,其他事件可能具有优先级,会延迟执行时间并推迟运行时。

    例子: 您可以创建一个延迟10秒的超时来向屏幕发出警报。它将被添加到事件堆栈中,并在所有当前事件被激发(导致一些延迟)后执行。然后,当处理超时时,浏览器仍然继续捕获其他事件,将它们添加到堆栈中,这将导致处理过程中的进一步延迟。如果用户单击,或者进行大量的ctrl+键入,那么他们的事件将优先于当前堆栈。你的10秒可以变成15秒,或者更长。


    也就是说,有很多方法可以伪造时间已经过去了。一种方法是在将setTimeout添加到堆栈之后立即执行setInterval。

    例子: 以10秒的延迟执行设置超时(将该延迟存储在全局中)。然后执行每秒运行一次的设置间隔,从延迟中减去1,并输出剩余的延迟。由于事件堆栈如何影响实际时间(如上所述),这仍然不准确,但会给出一个计数。


    简而言之,没有真正的方法来获得剩余的时间。只有几种方法可以尝试向用户传递评估。

        5
  •  3
  •   void main    10 年前

    这可能是一种更好的方法,而且,您不需要更改已经编写的代码:

    var getTimeout = (function() { // IIFE
        var _setTimeout = setTimeout, // Reference to the original setTimeout
            map = {}; // Map of all timeouts with their start date and delay
    
        setTimeout = function(callback, delay) { // Modify setTimeout
            var id = _setTimeout(callback, delay); // Run the original, and store the id
    
            map[id] = [Date.now(), delay]; // Store the start date and delay
    
            return id; // Return the id
        };
    
        return function(id) { // The actual getTimeLeft function
            var m = map[id]; // Find the timeout in map
    
            // If there was no timeout with that id, return NaN, otherwise, return the time left clamped to 0
            return m ? Math.max(m[1] - Date.now() + m[0], 0) : NaN;
        }
    })();
    

    …并模仿:

    var getTimeout=function(){var e=setTimeout,b={};setTimeout=function(a,c){var d=e(a,c);b[d]=[Date.now(),c];return d};return function(a){return(a=b[a])?Math.max(a[1]-Date.now()+a[0],0):NaN}}();
    
        6
  •  2
  •   John Watts    8 年前

    服务器端node.js特定

    以上这些都不适用于我,在检查超时对象之后,它看起来像是与进程启动时相关的一切。以下内容对我很有用:

    myTimer = setTimeout(function a(){console.log('Timer executed')},15000);
    
    function getTimeLeft(timeout){
      console.log(Math.ceil((timeout._idleStart + timeout._idleTimeout)/1000 - process.uptime()));
    }
    
    setInterval(getTimeLeft,1000,myTimer);
    

    输出:

    14
    ...
    3
    2
    1
    Timer executed
    -0
    -1
    ...
    
    node -v
    v9.11.1
    

    为简洁起见,编辑了输出,但这个基本函数给出了执行前或执行后的大致时间。正如其他人提到的,这些都不会是由于节点处理的方式,但是如果我想禁止一个运行不到1分钟的请求,并且我存储了计时器,我不明白为什么这不能作为一个快速检查工作。使用RefreshTimer在10.2+中处理对象可能很有趣。

        7
  •  0
  •   gblazex    16 年前

    不,但是您可以在函数中为动画设置自己的setTimeout/setInterval。

    说你的问题是这样的:

    function myQuestion() {
      // animate the progress bar for 1 sec
      animate( "progressbar", 1000 );
    
      // do the question stuff
      // ...
    }
    

    您的动画将由以下两个功能处理:

    function interpolate( start, end, pos ) {
      return start + ( pos * (end - start) );
    }
    
    function animate( dom, interval, delay ) {
    
          interval = interval || 1000;
          delay    = delay    || 10;
    
      var start    = Number(new Date());
    
      if ( typeof dom === "string" ) {
        dom = document.getElementById( dom );
      }
    
      function step() {
    
        var now     = Number(new Date()),
            elapsed = now - start,
            pos     = elapsed / interval,
            value   = ~~interpolate( 0, 500, pos ); // 0-500px (progress bar)
    
        dom.style.width = value + "px";
    
        if ( elapsed < interval )
          setTimeout( step, delay );
      }
    
      setTimeout( step, delay );
    }
    
        8
  •  0
  •   Vinay    11 年前

    如果有人回头看这个。我提出了一个超时和间隔管理器,可以让你在一个超时或间隔时间,以及做一些其他的事情。我会加上一点,使它更漂亮、更准确,但它似乎也相当有效(尽管我有更多的想法使它更准确):

    https://github.com/vhmth/Tock

        9
  •  0
  •   Sandeep Gupta    9 年前

    这个问题已经回答了,但我要补充一下。我刚想到。

    使用 setTimeout 在里面 recursion 如下:

    var count = -1;
    
    function beginTimer()
    {
        console.log("Counting 20 seconds");
        count++;
    
        if(count <20)
        {
            console.log(20-count+"seconds left");
            setTimeout(beginTimer,2000);
        }
        else
        {
            endTimer();
        }
    }
    
    function endTimer()
    {
        console.log("Time is finished");
    }
    

    我想代码是不言而喻的

        10
  •  0
  •   crokudripi    9 年前

    检查这个:

    class Timer {
      constructor(fun,delay) {
        this.timer=setTimeout(fun, delay)
        this.stamp=new Date()
      }
      get(){return ((this.timer._idleTimeout - (new Date-this.stamp))/1000) }
      clear(){return (this.stamp=null, clearTimeout(this.timer))}
    }
    

    制作计时器:

    let smtg = new Timer(()=>{do()}, 3000})
    

    获得保留:

    smth.get()
    

    清除超时

    smth.clear()
    
        11
  •  0
  •   Vitalik s2pac    9 年前
        (function(){
            window.activeCountdowns = [];
            window.setCountdown = function (code, delay, callback, interval) {
                var timeout = delay;
                var timeoutId = setTimeout(function(){
                    clearCountdown(timeoutId);
                    return code();
                }, delay);
                window.activeCountdowns.push(timeoutId);
                setTimeout(function countdown(){
                    var key = window.activeCountdowns.indexOf(timeoutId);
                    if (key < 0) return;
                    timeout -= interval;
                    setTimeout(countdown, interval);
                    return callback(timeout);
                }, interval);
                return timeoutId;
            };
            window.clearCountdown = function (timeoutId) {
                clearTimeout(timeoutId);
                var key = window.activeCountdowns.indexOf(timeoutId);
                if (key < 0) return;
                window.activeCountdowns.splice(key, 1);
            };
        })();
    
        //example
        var t = setCountdown(function () {
            console.log('done');
        }, 15000, function (i) {
            console.log(i / 1000);
        }, 1000);
    
        12
  •  0
  •   Jbur43    7 年前

    我在这里停下来寻找这个答案,但我想得太多了。如果你在这里是因为你只需要在设定时间的过程中跟踪时间,这里有另一种方法:

        var focusTime = parseInt(msg.time) * 1000
    
        setTimeout(function() {
            alert('Nice Job Heres 5 Schrute bucks')
            clearInterval(timerInterval)
        }, focusTime)
    
        var timerInterval = setInterval(function(){
            focusTime -= 1000
            initTimer(focusTime / 1000)
        }, 1000);