JavaScript è a thread singolo? In caso contrario, come posso ottenere l'accesso sincronizzato ai dati condivisi?

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

Domanda

Ho una pagina web con DIV con un gestore mouseover che ha lo scopo di mostrare una bolla informativa pop-up. Non voglio che più di una bolla informativa sia visibile alla volta. Ma quando l'utente sposta rapidamente il mouse su due elementi, a volte ottengo due bolle. Questo non dovrebbe accadere, perché il codice per mostrare un pop-up annulla il pop-up precedente.

Se si trattasse di un sistema multi-thread, il problema sarebbe ovvio: ci sono due thread che provano a mostrare un pop-up ed entrambi annullano i pop-up esistenti, quindi pop-up dei propri pop-up. Ma supponevo che JavaScript fosse sempre eseguito a thread singolo, il che impedirebbe questo. Ho sbagliato? I gestori di eventi funzionano in modo asincrono, nel qual caso ho bisogno di un accesso sincronizzato ai dati condivisi o dovrei invece cercare bug nel codice della libreria per annullare i popup?

Modificato per aggiungere:

  • La libreria in questione è SIMILE Timeline e la sua libreria Ajax;
  • Il gestore eventi chiama SimileAjax.DOM.cancelEvent (domEvt) , che presumo in base al nome annulla il gorgoglio degli eventi;
  • Solo per rendere le cose più complicate, quello che sto effettivamente facendo è iniziare un timeout che, se non cancellato da un moustout , mostra il pop-up, questo per prevenire lo sfarfallio dei pop-up fastidiosamente ma fastidiosamente avendo l'effetto contrario.

Ci darò un'altra occhiata e vedrò se riesco a capire dove sto sbagliando. : -)

È stato utile?

Soluzione

Sì, Javascript è a thread singolo. Anche con browser come Google Chrome, esiste un thread per scheda.

Senza sapere come si sta tentando di annullare un popup da un altro, è difficile dire quale sia la causa del problema.

Se i tuoi DIV sono nidificati l'uno nell'altro, potresti avere un propagazione di eventi .

Altri suggerimenti

Non conosco la libreria che stai utilizzando, ma se stai provando a visualizzare solo una descrizione di somesort alla volta ... usa un oggetto flyweight. Fondamentalmente un peso mosca è qualcosa che viene prodotto una volta e riutilizzato più volte. Pensa a una lezione di singleton. Quindi chiamate staticamente una classe che al primo richiamo crea automaticamente un oggetto di se stesso e lo memorizza. Uno di questi succede che tutti i dati statici fanno riferimento allo stesso oggetto e per questo motivo non si ottengono più suggerimenti o conflitti.

Uso ExtJS e fanno descrizioni comandi e finestre di messaggio come entrambi elementi flyweight. Spero che anche i tuoi framework abbiano elementi flyweight, altrimenti dovrai solo creare il tuo singleton e chiamarlo.

È a thread singolo nei browser. I gestori di eventi vengono eseguiti in modo asincrono in un thread, non bloccare non significa sempre multithread. Uno dei tuoi div è figlio dell'altro? Perché gli eventi si diffondono come bolle nell'albero del dom da bambino a genitore.

Simile a ciò che ha detto pkaeding, è difficile indovinare il problema senza vedere il markup e la sceneggiatura; tuttavia, mi permetto di dire che non stai arrestando correttamente la propagazione dell'evento e / o non stai nascondendo correttamente l'elemento esistente. Non so se stai usando un framework o no, ma ecco una possibile soluzione usando 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));

Naturalmente, questo potrebbe essere ulteriormente generalizzato ottenendo tutti gli elementi con, diciamo, la stessa classe, iterando attraverso di essi e applicando la stessa funzione di gestione degli eventi a ciascuno di essi.

Ad ogni modo, si spera che questo aiuti.

Cordiali saluti: A partire da Firefox 3 c'è un cambiamento abbastanza rilevante per questa discussione: i thread di esecuzione che causano richieste XMLHttpRequest sincrone vengono staccati (ecco perché l'interfaccia non si blocca lì durante le richieste sincrone) e l'esecuzione continua. Al completamento della richiesta sincrona, anche il thread continua. Non verranno eseguiti contemporaneamente, tuttavia basandosi sul presupposto che il singolo thread si arresti mentre una procedura sincrona (richiesta) in corso non è più applicabile.

È possibile che il display non si aggiorni abbastanza velocemente. A seconda della libreria JS che stai utilizzando, potresti essere in grado di ritardare leggermente il pop-up "mostra " effetto.

Ecco la versione funzionante, più o meno. Quando creiamo elementi, alleghiamo un evento mouseover :

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

Questo chiama una funzione che imposta un timeout (i timeout preesistenti per un elemento diverso vengono prima annullati):

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

Questo a sua volta mostra la bolla se si accende prima di essere annullato:

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

Questo sembra funzionare ora ho impostato il timeout su 200 ms anziché 100 ms. Non so perché un timeout troppo breve provochi la cosa multi-bolla, ma immagino che la coda degli eventi della finestra o qualcosa potrebbe ancora accadere mentre vengono disposti gli elementi appena aggiunti.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top