LINUX.ORG.RU

Помогите с таймерами

 


1

1

Приветствую. Выручайте, уже сломал голову. Суть задачи в том, что я пишу таймеры обратного отсчета, которые стартуют в определенный startTimestamp и останавливаются в определенный endTimestamp. Между началом и остановкой есть JS-интервал, который занимается выводом оставшегося времени до endTimestamp. Проблема в том, что иногда вывод неправильный, выводится вот что:

0 start 1482323367 index.html:52:13
0 input 13 index.html:57:1
0 input 12 index.html:57:1
0 input 11 index.html:57:1
0 input 10 index.html:57:1
0 input 9 index.html:57:1
0 input 8 index.html:57:1
0 input 7 index.html:57:1
0 input 6 index.html:57:1
0 input 5 index.html:57:1
0 input 4 index.html:57:1
0 input 3 index.html:57:1
0 input 2 index.html:57:1
0 input 1 index.html:57:1
1 start 1482323379 index.html:52:13
1 input 16 index.html:57:1
0 end 1482323379 index.html:81:17
1 input 14 index.html:57:1
1 input 13 index.html:57:1
1 input 12 index.html:57:1
1 input 11 index.html:57:1
1 input 10 index.html:57:1
1 input 9 index.html:57:1
1 input 8 index.html:57:1
1 input 7 index.html:57:1
1 input 6 index.html:57:1
1 input 5 index.html:57:1
1 input 4 index.html:57:1
1 input 3 index.html:57:1
1 input 2 index.html:57:1
1 input 1 index.html:57:1
2 start 1482323394 index.html:52:13
2 input 16 index.html:57:1
1 input 0 index.html:57:1
2 input 14 index.html:57:1
1 end 1482323396 index.html:81:17
2 input 13 index.html:57:1
2 input 12 index.html:57:1
2 input 11 index.html:57:1
2 input 10 index.html:57:1
2 input 9 index.html:57:1
2 input 8 index.html:57:1
2 input 7 index.html:57:1
2 input 6 index.html:57:1
2 input 5 index.html:57:1
2 input 4 index.html:57:1
2 input 3 index.html:57:1
2 input 2 index.html:57:1
2 input 1 index.html:57:1
2 input 0 index.html:57:1
2 end 1482323411
Иными словами, когда исчезает первый таймер, то второй начинает обратный отсчет с 16 вместо 15, а потом перескакивает сразу на 14, а должно быть - 15 -> 14 -> 13 и т.д. Самое интересное, что проблема то исчезает, то появляется, как-будто это вызвано какими-то задержками при выполнении кода.

Заранее благодарю всех отписавшихся.

upd: лол, сейчас архив с кодом скину

★★

Ну, во-первых, тебе сначала стоит понять, как работает event loop в js.

Твои эти задержки в 1000 или 3600 по факту не являются таковыми.

Запуск событий повешенных на таймер, будут выполняться при idle, если ты куда-то воткнешь блокирующую операцию на 10 минут, то твой отложенный таймер, который должен сработать по твоим расчетам через секунду, сработает только когда блокирующая операция завершится.

На моей машине твой скрипт не глючит с 16, за тем лишь исключением, что отсчет следующего таймера начинается раньше, чем закончился предыдущий. В твое логе это тоже есть.

Возможно тебе стоит фиксировать истекшее время по нормальным таймстемпам (Date), а не полагаться лишь на таймеры, потому что они тебе ничего и никогда не гарантировали.

int64 ()

1. У setTimeout() точность фиговая, порядка 10ms. То есть поставить-то ты на 1ms можешь, но получишь совсем не то.
2. Поэтому иногда лучше использовать setInterval()
3. Если надо очень много таймеров, тогда смотри как в ноде это сделано - много двухуровневых списков и один таймер который их молотит.

PS. Осиль гитхаб для выкладывания исходников.

Vit ★★★★★ ()

По мне так, ты что с setTimeout/setInterval, что с Date будешь переодически отстреливать себе ногу.

Мне не понятно, чего ты хочешь в результате добиться этим кодом (и каковы к нему требования), но все таки, посмотри в сторону другого подхода - передавай новому таймеру явно сообщения о том, что предыдущий таймер отработал, не завязываясь на время.

irr123 ()
    var timersSlot = $('#timers');

    var timers = [];

    var timerToStart = 0;

    var serverTime = 0;

    function timeLeftToString(timeLeft, customizationCallback) {
        var temp = timeLeft;

        // var days = Math.trunc(temp / 86400);

        // temp = temp - (days * 86400);

        var hours = Math.trunc(temp / 3600);

        temp = temp - (hours * 3600);

        var minutes = Math.trunc(temp / 60);

        temp = temp - (minutes * 60);

        var seconds = temp;

        if (customizationCallback !== null) {
            return customizationCallback(timeLeft, hours, minutes, seconds);
        }

        // days + '--' + hours + '--' + minutes + '--' + seconds;
        return hours + '--' + minutes + '--' + seconds;
    }

    function nextTimer() {
        if (timers[timerToStart] !== undefined) {
            var interval = null;

            var currentTimer = timers[timerToStart];

            // копируем из оригинала, чтобы не переписывать его
            var timerTime = currentTimer.time;

            var intervalFunction = function () {
                // timeLeftToString может принимать вторым аргументом
                // callback который может переопределить вывод
                // если нужно использовать стандартный вывод, то передайте
                // null
                var customView = function (timeLeft, hours, minutes, seconds) {
                    return 'осталось ' + timeLeft + ' секунд(-ы)';
                };

                currentTimer.body.html(timeLeftToString(timerTime, customView));

                if (timerTime === 0) {
                    clearInterval(interval);

                    timerToStart++;

                    currentTimer.body.remove();

                    nextTimer();
                }

                timerTime--;
            };

            // интервал сработает через 1 секунду, а его
            // функция должна уже сейчас выполнится
            intervalFunction();

            interval = setInterval(intervalFunction, 1000);
        } else {
            timersSlot.html('Нет активных таймеров');
        }
    }

    // другая смысловая нагрузка, а по факту - то же самое
    function timersStart() {
        nextTimer();
    }

//    var ajaxStartTime = new Date();
//
//    $.ajax({
//        url: 'gettimers.php',
//        success: function (response) {
//            try {
//                var responseBody = JSON.parse(response);

                var responseBody = {"server_time":1482323367,"timers":[{"timestamp":"1482323380","field":"1"},{"timestamp":"1482323395","field":"1"},{"timestamp":"1482323410","field":"1"}]};

//                var delay = Math.trunc((new Date().getTime() - ajaxStartTime.getTime()) / 1000);

                serverTime = responseBody['server_time'];// + delay;

                setInterval(function () {
                    serverTime++;
                }, 1000);

                var jsonTimers = responseBody['timers'];

                if (jsonTimers.length > 0) {
                    for (var i = 0; i < jsonTimers.length; i++) {
                        var timerBody = $(document.createElement('div'));

                        timerBody.attr('id', 'timer_' + i);

                        timersSlot.append(timerBody);

                        var isFirst = i === 0;

                        var startTimestamp = (isFirst) ? serverTime : parseInt(jsonTimers[i - 1].timestamp);

                        var endTimestamp = parseInt(jsonTimers[i].timestamp);

                        // время для обратного отсчета
                        var timerTime = endTimestamp - startTimestamp;

                        timers.push({
                            body: timerBody,
                            time: timerTime,
                            onStart: function() {},
                            onEnd: function() {}
                        });

                        timerBody.html(timeLeftToString(timerTime, null));
                    }

                    timersStart();
                }
//            } catch (e) {
//                alert('Ошибка: ' + e.message + "\n" + e.stack);
//            }
//        },
//        error: function (data) {
//            alert('Ошибка Ajax: ' + data.status + ' ' + data.statusText);
//        }
//    });

Теперь таймеры работают реально по-очереди. Пока полет нормальный, никаких 16-14 не выдавало.

Razip ★★ ()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.