Является ли JavaScript однопоточным? Если нет, как я могу получить синхронизированный доступ к общим данным?
У меня есть веб-страница с DIV
с mouseover
обработчик, предназначенный для отображения всплывающего информационного пузыря. Я не хочу, чтобы одновременно отображался более одного информационного пузыря. Но когда пользователь быстро перемещает мышь над двумя предметами, я иногда получаю два пузыря. Этого не должно быть, потому что код для показа всплывающего окна отменяет предыдущее всплывающее окно.
Если бы это была многопоточная система, то проблема была бы очевидна: два потока пытаются показать всплывающее окно, и оба они отменяют существующие всплывающие окна, а затем всплывают свои собственные всплывающие окна. Но я предположил, что JavaScript всегда выполняется однопоточным, что предотвратит это. Я ошибся? Работают ли обработчики событий асинхронно, и в этом случае мне нужен синхронизированный доступ к общим данным, или я должен вместо этого искать ошибки в коде библиотеки для отмены всплывающих окон?
Отредактировано, чтобы добавить:
- Рассматриваемая библиотека - SIMILE Timeline и ее библиотека Ajax;
- Обработчик события вызывает
SimileAjax.DOM.cancelEvent(domEvt)
, который, как я полагаю, основан на названии, отменяет всплеск событий; - Просто чтобы усложнить ситуацию, я на самом деле начинаю тайм-аут, который не отменяется
moustout
показывает всплывающее окно, это предназначено для предотвращения всплывающих всплывающих окон, раздражающих, но раздражающе имеющих обратный эффект.
Я еще раз попробую и посмотрю, смогу ли я понять, где я иду не так.:-)
7 ответов
Да, Javascript является однопоточным. Даже в таких браузерах, как Google Chrome, на вкладке есть один поток.
Не зная, как вы пытаетесь отменить одно всплывающее окно от другого, трудно сказать, в чем причина вашей проблемы.
Если ваши DIV вложены друг в друга, у вас может возникнуть проблема распространения событий.
Я не знаю библиотеку, которую вы используете, но если вы пытаетесь отобразить только одну всплывающую подсказку за раз... используйте объект с весом. В основном, легкий вес - это то, что делается один раз и используется снова и снова. Подумайте об одном классе. Таким образом, вы вызываете класс статически, который при первом вызове автоматически создает сам объект и сохраняет его. Как только это происходит, каждый статический объект ссылается на один и тот же объект, и из-за этого вы не получаете несколько всплывающих подсказок или конфликтов.
Я использую ExtJS, и они делают всплывающие подсказки и окна сообщений как элементы flyweight. Я надеюсь, что ваши фреймворки также имеют элементы flyweight, иначе вам просто нужно будет создать свой собственный синглтон и вызвать его.
Это однопоточный в браузерах. Обработчики событий работают асинхронно в одном потоке, неблокирование не всегда означает многопоточность. Является ли один из ваших divs дочерью другого? Потому что события распространяются как пузыри в домене дерева от ребенка к родителю.
К сведению: Начиная с Firefox 3, в этом обсуждении произошли изменения, в значительной степени относящиеся к этому: потоки выполнения, вызывающие синхронные запросы XMLHttpRequest, отсоединяются (именно поэтому интерфейс не останавливается там во время синхронных запросов), и выполнение продолжается. По завершении синхронного запроса его поток также продолжается. Они не будут выполняться одновременно, однако полагаясь на предположение, что один поток останавливается, пока синхронная процедура (запрос) больше не применяется.
Подобно тому, что сказал 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));
Конечно, это можно обобщить, получив все элементы, скажем, с одним и тем же классом, итерируя их и применив к каждому из них одну и ту же функцию обработки событий.
В любом случае, надеюсь, это поможет.
Вот рабочая версия, более или менее. При создании предметов мы прикрепляем 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 мс. Не уверен, почему слишком короткое время ожидания приводит к возникновению многопузырьковой вещи, но я предполагаю, что очереди оконных событий или что-то еще может происходить, пока выкладываются новые элементы.
Возможно, дисплей не обновляется достаточно быстро. В зависимости от используемой вами библиотеки JS, вы можете добавить небольшую задержку для всплывающего эффекта "шоу".