найти время, оставшееся в setTimeout ()? - PullRequest
81 голосов
/ 30 июня 2010

Я пишу некоторый 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);
}

tl; 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 ]

51 голосов
/ 28 мая 2012

Только для записи, есть способ получить время, оставшееся в файле 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, я подумал, что это замечание может быть полезно для людей, которые ищут решение для узла.

47 голосов
/ 23 декабря 2013

РЕДАКТИРОВАТЬ: Я действительно думаю, что я сделал еще лучше: 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()
25 голосов
/ 30 июня 2010

Если вы не можете изменить код библиотеки, вам нужно переопределить 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, чтобы вы могли отсоединить слушателя, если время ожидания сброшено.

3 голосов
/ 16 июля 2018

Спецификация 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 минуты назад, и я сохранил таймер, я не понимаю, почему это не будетработать как быстрая проверка.Может быть интересно жонглировать объектами с помощью освежителя в 10.2+.

3 голосов
/ 03 апреля 2016

Вот, возможно, еще лучший способ сделать это, к тому же вам не нужно менять уже написанный код:

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}}();
3 голосов
/ 30 июня 2010

Стеки событий Javascript не работают так, как вы думаете.

Когда создается событие тайм-аута, оно добавляется в очередь событий, но другие события могут иметь приоритет во время его запуска, задержкавремя выполнения и откладывание времени выполнения.

Пример: Вы создаете тайм-аут с задержкой 10 секунд, чтобы что-то предупредить на экране.Он будет добавлен в стек событий и будет выполнен после запуска всех текущих событий (что вызывает некоторую задержку).Затем, когда время ожидания обработано, браузер все еще продолжает захватывать другие события, добавляя их в стек, что вызывает дальнейшие задержки в обработке.Если пользователь щелкает или выполняет много нажатий клавиш Ctrl +, его события имеют приоритет над текущим стеком.Ваши 10 секунд могут превратиться в 15 секунд или дольше.


Как говорится, есть много способов подделать, сколько времени прошло.Одним из способов является выполнение setInterval сразу после добавления setTimeout в стек.

Пример: Выполнить заданное время ожидания с 10-секундной задержкой (сохранить эту задержку в глобальном формате).Затем выполните setInterval, который выполняется каждую секунду, чтобы вычесть 1 из задержки и вывести оставшуюся задержку.Из-за того, как стек событий может влиять на фактическое время (описано выше), это все еще не будет точным, но дает счет.


Короче говоря, нет никакого реального способа получить оставшиесявремя.Есть только способы попытаться передать оценку пользователю.

0 голосов
/ 22 декабря 2018

Я зашёл сюда в поисках этого ответа, но задумался над моей проблемой.Если вы находитесь здесь, потому что вам просто нужно следить за временем, пока вы используете setTimeout, вот еще один способ сделать это:

    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);
0 голосов
/ 27 февраля 2017
    (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);
0 голосов
/ 01 февраля 2017

Отметьте это:

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()
0 голосов
/ 25 октября 2016

На вопрос уже дан ответ, но я добавлю свой бит. Это произошло со мной.

Используйте 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");
}

Полагаю, код не требует пояснений

...