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

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

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

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

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

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

Ответы [ 7 ]

9 голосов
/ 02 октября 2008

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

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

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

2 голосов
/ 20 мая 2009

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

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

1 голос
/ 02 октября 2008

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

1 голос
/ 02 октября 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));

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

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

1 голос
/ 02 октября 2008

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

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

0 голосов
/ 02 октября 2008

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

...