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

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

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

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

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

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


person Community    schedule 02.10.2008    source источник


Ответы (7)


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

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

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

person pkaeding    schedule 02.10.2008
comment
Ну, я думаю, что Chrome выполняет AJAX с несколькими потоками, но они, вероятно, синхронизируются, прежде чем выполнять обратный вызов в ваших сценариях. - person Robert Gould; 02.10.2008
comment
Да, если вы делаете асинхронный вызов XML-HTTP, он должен быть в потоке, отличном от основного потока JS, иначе он не будет асинхронным. Однако для всех целей и задач существует только один поток. Вам не нужно беспокоиться о синхронизации в javascript. - person pkaeding; 02.10.2008
comment
Все браузеры используют AJAX в нескольких потоках — вот что означает бит асинхронности. Как правило, ни один браузер не обрабатывает сетевой трафик синхронно, если только он не может его избежать (единственный способ запустить его — через синхронный XHR), но это отличается от JS — JS по-прежнему работает в основном потоке пользовательского интерфейса. - person olliej; 03.10.2008
comment
Обновление: 3 года спустя, с появлением HTML5 JS теперь многопоточный. - person Ben; 19.02.2011

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

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

person Community    schedule 20.05.2009

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

person Vasil    schedule 02.10.2008

Подобно тому, что сказал 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));

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

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

person Tom    schedule 02.10.2008

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

person Sergey Ilinsky    schedule 02.10.2008
comment
Можете ли вы указать источник вашего последнего утверждения? - person Andrea Zilio; 19.08.2010

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

person Toby Hede    schedule 02.10.2008

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

person pdc    schedule 02.10.2008