Вопрос о состоянии гонки в Javascript - PullRequest
1 голос
/ 04 декабря 2009

РЕДАКТИРОВАТЬ: Я разобрался с ответом на оригинальный вопрос YUI3, который я разместил здесь, но он привел к другому, и вместо того, чтобы начинать новую тему, я подумал, что просто добавлю его сюда. Пожалуйста, прокрутите вниз до нового вопроса (он выделен жирным шрифтом).

Оригинальный вопрос: У меня есть некоторые проблемы при создании таймера обратного отсчета JavaScript внутри определения YUI, я думаю, что это связано с определением объема объекта. Вот мой код:

YUI({combine: true, timeout: 10000}).use("node", function (Y) {
    var timer = new function(){};
    Y.augment(timer, Y.EventTarget);
    timer.on('timer:down', function() {
        Y.log('timer down event fired', 'event');
        Y.Lang.later(1000, Y, timer_trigger());
    });
    timer.on('timer:end', function() {
        Y.log('timer end event fired', 'event');
    });

    var timer_from;

    function startTimer(seconds){ // start a coundown from seconds to 0
        timer_from = seconds;
        timer_trigger();
    }

    function timer_display(){
        var mins = Math.floor(timer_from/60);
        var secs = timer_from - mins*60;
        var secsDisp = secs;
        if(secs<10){
            secsDisp = '0' + secs;
        }
        Y.one('#timer').set('innerHTML', mins + ':' + secsDisp);
    }

    function timer_trigger(){
        Y.log('timer from is: '+timer_from);
        if(timer_from > 0){
            timer_from--;
            timer_display();
            if(timer_from > 0){
                timer.fire('timer:down');
            }
        } else {
            timer.fire('timer:end');
        }
    }

    function initializePage(){
        startTimer(900);
    }


});

Ошибка, которую я получаю, заключается в том, что она не ждет 1000 мс, как будто я прошу ее вызвать timer_trigger(), и Safari в итоге спрашивает меня, хочу ли я прекратить выполнение кода. Когда я делаю это через несколько секунд после загрузки страницы, таймер уже выключен примерно до 3, 4 минут. Я также пытался использовать setTimeout, но это также дает тот же результат. Кто-нибудь может помочь? Буду очень признателен!

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

Так что, очевидно, мой код создавал условие гонки, и все, что мне нужно было сделать, чтобы это исправить, это:

setTimeout(function(){ 
    timer_trigger();
}, 1000);

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

Как работает многопоточность в JavaScript и что является причиной моего состояния гонки и почему незначительное изменение в коде исправило ошибку, которую я имел?

Ответы [ 2 ]

3 голосов
/ 04 декабря 2009

Проблема не в состоянии гонки. Причина, по которой дополнительный вызов setTimeout «исправляет» ваш код, заключается в логическом недостатке timer_trigger. Рассмотрим, что происходит в случае, когда timer_from равно 1, когда вызывается функция. Ни таймер: вниз, ни таймер: конец не сработает.

function timer_trigger(){
    Y.log('timer from is: '+timer_from);
    if(timer_from > 0){      // Since timer_from is 1, the if block is entered
        timer_from--;        // timer_from is 0
        timer_display();
        if(timer_from > 0){  // This block is not entered, but it has no matching else
            timer.fire('timer:down');
        }
    } else {                 // The matching if block was entered, so this is not
        timer.fire('timer:end');
    }
}

Вы добавили этот код:

setTimeout(function(){ 
    timer_trigger();
}, 1000);

Это вызывает повторный вызов timer_trigger с timer_from, уже установленным в 0, что позволяет выполнить блок else.

2 голосов
/ 04 декабря 2009

Также обратите внимание, что

Y.Lang.later(1000, Y, timer_trigger());

немедленно выполняет timer_trigger и передает возвращаемое значение в Y.Lang.later. Вы, вероятно, имели в виду

Y.Lang.later(1000, Y, timer_trigger);
...