JavaScriptはシングルスレッドですか?そうでない場合、共有データへの同期アクセスを取得するにはどうすればよいですか?
-
03-07-2019 - |
質問
ポップアップ情報バブルを表示するための mouseover
ハンドラーを備えた DIV
sのあるWebページがあります。一度に複数の情報バブルを表示したくありません。しかし、ユーザーが2つのアイテム上でマウスをすばやく動かすと、2つのバブルが表示されることがあります。ポップアップを表示するためのコードが前のポップアップをキャンセルするため、これは起こりません。
これがマルチスレッドシステムである場合、問題は明らかです。ポップアップを表示しようとしているスレッドが2つあり、両方とも既存のポップアップをキャンセルしてから、独自のポップアップをポップアップします。しかし、JavaScriptは常にシングルスレッドで実行されると想定していたため、これを防ぐことができました。私が間違っている?イベントハンドラーは非同期で実行されていますか?その場合、共有データへの同期アクセスが必要ですか、代わりにポップアップをキャンセルするためのライブラリコードのバグを探す必要がありますか?
追加して編集:
- 問題のライブラリは SIMILEタイムラインとそのAjaxライブラリです。
- イベントハンドラーは、
SimileAjax.DOM.cancelEvent(domEvt)
を呼び出します。名前に基づいて、イベントのバブリングがキャンセルされると思います。 - 物事をもっと複雑にするために、私が実際にやっているのは、
moustout
によってキャンセルされない場合、ポップアップが表示されるタイムアウトを開始することです。迷惑なことに逆の効果があります。
別の突っ込みをして、どこが間違っているのか解決できるかどうかを確認します。 :-)
解決
はい、Javascriptはシングルスレッドです。 Google Chromeなどのブラウザでも、タブごとに1つのスレッドがあります。
あるポップアップを別のポップアップからキャンセルする方法を知らなくても、問題の原因を特定することは困難です。
DIVが相互にネストされている場合、イベント伝播の問題。
他のヒント
使用しているライブラリがわかりませんが、一度にある種のツールチップを1つだけ表示しようとしている場合は、フライウェイトオブジェクトを使用してください。基本的に、フライウェイトは一度作られて何度も使用されるものです。シングルトンクラスを考えてください。そのため、クラスを静的に呼び出して、最初に呼び出されたときにそれ自体のオブジェクトを自動的に作成して保存します。これは、すべての静的すべてが同じオブジェクトを参照するために発生します。これにより、複数のツールチップや競合は発生しません。
ExtJSを使用し、ツールヒントとメッセージボックスを両方のフライウェイト要素として使用します。フレームワークにもフライウェイト要素が含まれていることを期待しています。そうでなければ、独自のシングルトンを作成して呼び出す必要があります。
ブラウザではシングルスレッドです。イベントハンドラーは1つのスレッドで非同期に実行されていますが、非ブロッキングは常にマルチスレッドを意味するわけではありません。あなたのdivの1つは他のdivの子ですか?なぜなら、イベントは子供から親へとdomツリーの泡のように広がるからです。
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));
もちろん、これは、たとえば同じクラスを持つすべての要素を取得し、それらを繰り返し処理し、それぞれに同じイベント処理関数を適用することにより、さらに一般化できます。
どちらの方法でも、うまくいけばこれが役立つでしょう。
FYI:Firefox 3の時点では、この議論にかなり関連する変更があります:同期XMLHttpRequestリクエストを引き起こす実行スレッドが切り離され(同期リクエスト中にインターフェースがそこでフリーズしない理由です)、実行が継続されます。同期要求が完了すると、そのスレッドも継続します。同時に実行されることはありませんが、同期プロシージャ(要求)が発生している間はシングルスレッドが停止するという前提に依存しています。
ディスプレイのリフレッシュ速度が十分でない可能性があります。使用しているJSライブラリによっては、ポップアップの「show」にわずかな遅延を設定できる場合があります。効果。
これは、作業バージョンです。アイテムを作成するとき、 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(...);
};
これは、タイムアウトを100ミリ秒ではなく200ミリ秒に設定したので、動作しているようです。タイムアウトが短すぎるとマルチバブルが発生する理由はわかりませんが、ウィンドウイベントのキューイングや、新しく追加された要素のレイアウト中に何かがまだ発生している可能性があります。