JavaScript est-il un seul thread? Sinon, comment puis-je obtenir un accès synchronisé aux données partagées?

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

Question

J'ai une page Web avec DIV avec un gestionnaire mouseover destiné à afficher une bulle d'informations contextuelle. Je ne veux pas que plus d'une bulle d'information soit visible à la fois. Mais lorsque l'utilisateur déplace rapidement la souris sur deux éléments, il m'arrive parfois d'obtenir deux bulles. Cela ne devrait pas se produire, car le code permettant d'afficher une fenêtre contextuelle annule la fenêtre contextuelle précédente.

S'il s'agissait d'un système multi-thread, le problème serait évident: deux threads essaient d'afficher une fenêtre contextuelle. Ils annulent les fenêtres contextuelles existantes, puis affichent leurs propres fenêtres contextuelles. Mais j'ai supposé que JavaScript est toujours exécuté avec un seul thread, ce qui empêcherait cela. Ai-je tort? Les gestionnaires d'événements fonctionnent-ils de manière asynchrone, auquel cas j'ai besoin d'un accès synchronisé aux données partagées, ou devrais-je plutôt rechercher des bogues dans le code de la bibliothèque pour annuler les fenêtres contextuelles?

Modifié pour ajouter:

  • La bibliothèque en question est Chronologie SIMILE et sa bibliothèque Ajax;
  • Le gestionnaire d'événements appelle SimileAjax.DOM.cancelEvent (domEvt) , ce qui, je suppose, basé sur son nom annule la propagation d'événements;
  • Simplement pour rendre les choses plus compliquées, ce que je suis en train de faire, c’est de commencer une temporisation qui, si elle n’est pas annulée par un moustout , affiche la fenêtre contextuelle, ceci dans le but d’empêcher le scintillement des fenêtres contextuelles. mais avoir l’effet inverse.

Je vais avoir un autre coup de fourche et voir si je peux trouver où je vais mal. : -)

Était-ce utile?

La solution

Oui, Javascript est à thread unique. Même avec des navigateurs tels que Google Chrome, il existe un fil par onglet.

Sans savoir comment vous essayez d'annuler une fenêtre contextuelle, il est difficile de dire quelle est la cause de votre problème.

Si vos DIV sont imbriquées l'une dans l'autre, vous pouvez avoir un propagation d'événement issue.

Autres conseils

Je ne connais pas la bibliothèque que vous utilisez, mais si vous essayez uniquement d'afficher une info-bulle de certains fichiers à la fois ... utilisez un objet flyweight. Fondamentalement, un flyweight est quelque chose qui est fait une fois et utilisé encore et encore. Pensez à une classe singleton. Vous appelez donc statiquement une classe qui, lorsqu'elle est invoquée pour la première fois, crée automatiquement un objet et le stocke. Cela se produit lorsque toutes les statiques font référence au même objet. Par conséquent, vous n’obtenez pas plusieurs info-bulles ou conflits.

J'utilise ExtJS et ils font des info-bulles et des boîtes de message comme éléments flyweight. J'espère que vos frameworks ont également des éléments flyweight, sinon vous devrez créer votre propre singleton et l'appeler.

Il s’agit d’un seul thread dans les navigateurs. Les gestionnaires d'événements s'exécutent de manière asynchrone dans un seul thread, non-blocage ne signifie pas toujours multithread. L'un de vos divs est-il un enfant de l'autre? Parce que les événements se propagent comme des bulles dans l’arborescence des doms d’un enfant à un parent.

Semblable à ce que pkaeding a dit, il est difficile de deviner le problème sans consulter votre balisage et votre script. Cependant, j'oserais dire que vous n'arrêtez pas correctement la propagation d'événement et / ou que vous ne cachez pas correctement l'élément existant. Je ne sais pas si vous utilisez un framework ou non, mais voici une solution possible utilisant 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));

Bien sûr, cela pourrait être généralisé davantage en récupérant tous les éléments avec, disons, la même classe, en les itérant, et en appliquant la même fonction de gestion des événements à chacun d'eux.

Quoi qu'il en soit, cela aide, espérons-le.

Pour votre information: à partir de Firefox 3, un changement relativement pertinent concerne cette discussion: les threads d'exécution générant des requêtes XMLHttpRequest synchrones se détachent (c'est pourquoi l'interface ne s'y bloque pas lors de requêtes synchrones) et l'exécution se poursuit. Une fois la demande synchrone terminée, son thread continue également. Ils ne seront pas exécutés en même temps, mais s’appuyer sur l’hypothèse selon laquelle un seul thread s’arrête alors qu’une procédure synchrone (demande) en cours n’est plus applicable.

Il se peut que l’affichage ne soit pas rafraîchissant assez rapidement. En fonction de la bibliothèque JS que vous utilisez, vous pourrez peut-être mettre un petit délai dans la fenêtre contextuelle "show". effet.

Voici la version de travail, plus ou moins. Lors de la création d'éléments, nous associons un événement mouseover :

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

Ceci appelle une fonction qui définit un délai d'expiration (les délais pré-existants pour un élément différent sont d'abord annulés):

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);
}

Ceci affiche à son tour la bulle si elle se déclenche avant d'être annulée:

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(...);
};

Cela semble fonctionner maintenant, j'ai réglé le délai d'attente à 200 ms au lieu de 100 ms. Vous ne savez pas pourquoi un délai trop court entraîne la création de plusieurs bulles, mais j'imagine que la mise en file d'attente d'événements de fenêtre ou quelque chose pourrait encore se produire pendant la présentation des nouveaux éléments ajoutés.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top