Является ли JavaScript однопоточным?Если нет, то как мне получить синхронизированный доступ к общим данным?

StackOverflow https://stackoverflow.com/questions/161783

Вопрос

У меня есть веб-страница с DIVс mouseover обработчик, предназначенный для отображения всплывающего информационного пузыря.Я не хочу, чтобы одновременно было видно более одного информационного пузыря.Но когда пользователь быстро перемещает указатель мыши по двум элементам, у меня иногда появляются два пузырька.Этого не должно произойти, поскольку код показа всплывающего окна отменяет предыдущее всплывающее окно.

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

Отредактировано, чтобы добавить:

  • Речь идет о библиотеке ПОДОБНО и его библиотека Ajax;
  • Обработчик событий вызывает SimileAjax.DOM.cancelEvent(domEvt), который, как я предполагаю, исходя из названия, отменяет всплывание событий;
  • Чтобы еще больше усложнить задачу: на самом деле я запускаю тайм-аут, который, если его не отменить, moustout показывает всплывающее окно, это предназначено для предотвращения раздражающего мерцания всплывающих окон, но раздражающего действия, имеющего обратный эффект.

Я еще раз покопаюсь и посмотрю, смогу ли я понять, где я ошибаюсь.:-)

Это было полезно?

Решение

Да, Javascript однопоточный.Даже в таких браузерах, как Google Chrome, на каждую вкладку приходится один поток.

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

Если ваши DIV вложены друг в друга, у вас может возникнуть распространение событий проблема.

Другие советы

Я не знаю, какую библиотеку вы используете, но если вы пытаетесь отобразить только одну всплывающую подсказку за раз...используйте легковесный объект.По сути, наилегчайший вес — это то, что делается один раз и используется снова и снова.Подумайте о одноэлементном классе.Таким образом, вы статически вызываете класс, который при первом вызове автоматически создает объект и сохраняет его.Это происходит каждый раз, когда все статические объекты ссылаются на один и тот же объект, и из-за этого вы не получаете несколько всплывающих подсказок или конфликтов.

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

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

Как и сказал pkaeding, трудно угадать проблему, не видя вашей разметки и сценария;однако я бы рискнул сказать, что вы неправильно останавливаете распространение событий и/или неправильно скрываете существующий элемент.Я не знаю, используете ли вы фреймворк или нет, но вот возможное решение с использованием Prototype:

// maintain a reference to the active div bubble
this.oActiveDivBubble = null;

// event handler for the first div
$('exampleDiv1').observe('mouseover', function(evt) {
    evt.stop();
    if(this.oActiveDivBubble ) {
        this.oActiveDivBubble .hide();
    }
    this.oActiveDivBubble = $('exampleDiv1Bubble');
    this.oActiveDivBubble .show();

}.bind(this));

// event handler for the second div
$('exampleDiv2').observe('mouseover'), function(evt) {
    evt.stop();
    if(this.oActiveDivBubble) {
        this.oActiveDivBubble.hide();
    }
    this.oActiveDivBubble = $('exampleDiv2Bubble');
    this.oActiveDivBubble .show();
}.bind(this));

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

В любом случае, надеюсь, это поможет.

К вашему сведению:Начиная с Firefox 3, есть изменение, очень важное для этого обсуждения:потоки исполнения, вызывающие синхронные запросы XMLHttpRequest, отсоединяются (поэтому интерфейс там не зависает во время синхронных запросов) и выполнение продолжается.После завершения синхронного запроса его поток также продолжается.Они не будут выполняться одновременно, однако исходя из предположения, что один поток останавливается, пока происходит синхронная процедура (запрос), это больше не применимо.

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

Вот более-менее рабочая версия.При создании предметов мы прикрепляем mouseover событие:

var self = this;
SimileAjax.DOM.registerEvent(labelElmtData.elmt, "mouseover", function (elt, domEvt, target) {
    return self._onHover(labelElmtData.elmt, domEvt, evt);
});

Это вызывает функцию, которая устанавливает тайм-аут (сначала отменяются ранее существовавшие тайм-ауты для другого элемента):

MyPlan.EventPainter.prototype._onHover = function(target, domEvt, evt) {            
    ... calculate x and y ...
    domEvt.cancelBubble = true;
    SimileAjax.DOM.cancelEvent(domEvt);
    this._futureShowBubble(x, y, evt);

    return false;
}
MyPlan.EventPainter.prototype._futureShowBubble = function (x, y, evt) {
    if (this._futurePopup) {
        if (evt.getID() == this._futurePopup.evt.getID()) {
            return;
        } else {
            /* We had queued a different event's pop-up; this must now be cancelled. */
            window.clearTimeout(this._futurePopup.timeoutID);
        } 
    }
    this._futurePopup = {
        x: x,
        y: y,
        evt: evt
    };    
    var self = this;
    this._futurePopup.timeoutID =  window.setTimeout(function () {
            self._onTimeout();
    }, this._popupTimeout);
}

Это, в свою очередь, показывает пузырь, если он срабатывает перед отменой:

MyPlan.EventPainter.prototype._onTimeout = function () {
    this._showBubble(this._futurePopup.x, this._futurePopup.y, this._futurePopup.evt);

};

MyPlan.EventPainter.prototype._showBubble = function(x, y, evt) {
    if (this._futurePopup) {
        window.clearTimeout(this._futurePopup.timeoutID);
        this._futurePopup = null;
    }        
    ...

    SimileAjax.WindowManager.cancelPopups();
    SimileAjax.Graphics.createBubbleForContentAndPoint(...);
};

Кажется, теперь это работает, я установил тайм-аут на 200 мс, а не на 100 мс.Не знаю, почему слишком короткий тайм-аут приводит к появлению нескольких пузырьков, но я предполагаю, что очередь оконных событий или что-то еще может происходить во время размещения вновь добавленных элементов.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top