Is JavaScript single threaded? If not, how do I ge

2019-01-24 14:20发布

I have a web page with DIVs with a mouseover handler that is intended to show a pop-up information bubble. I don't want more than one info bubble to be visible at a time. But when the user moves the mouse rapidly over two items, I sometimes get two bubbles. This should not happen, because the code for showing a pop-up cancels the previous pop-up.

If this were a multi-threaded system then the problem would be obvious: there are two threads trying to show a pop-up, and they both cancel existing pop-ups then pop up their own pop-ups. But I assumed JavaScript is always run single-threaded, which would prevent this. Am I wrong? Are event handlers running asynchronously, in which case I need synchronized access to shared data, or should I instead be looking for bugs in the library code for cancelling pop-ups?

Edited to add:

  • The library in question is SIMILE Timeline and its Ajax library;
  • The event handler does call SimileAjax.DOM.cancelEvent(domEvt), which I assume based on the name cancels the bubbling of events;
  • Just to make thing s more complicated, what I am actually doing is starting a timeout that if not cancelled by a moustout shows the pop-up, this being intended to prevent pop-ups flickering annoyingly but annoyingly having the reverse effect.

I'll have another poke at it and see if I can work out where I am going wrong. :-)

7条回答
唯我独甜
2楼-- · 2019-01-24 14:36

It is single threaded in browsers. Event handlers are running asynchroniously in one thread, non blocking doesn't allways mean multithreaded. Is one of your divs a child of the other? Because events spread like bubbles in the dom tree from child to parent.

查看更多
迷人小祖宗
3楼-- · 2019-01-24 14:38

Similar to what pkaeding said, it's hard to guess the problem without seeing your markup and script; however, I'd venture to say that you're not properly stopping the event propagation and/or you're not properly hiding the existing element. I don't know if you're using a framework or not, but here's a possible solution using 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));

Of course, this could be generalized further by getting all of the elements with, say, the same class, iterating through them, and applying the same event handling function to each of them.

Either way, hopefully this helps.

查看更多
冷血范
4楼-- · 2019-01-24 14:43

I don't know the library you are using, but if you are only trying to display one tooltip of somesort at a time... use a flyweight object. Basically a flyweight is something that is made once and used over and over again. Think of a singleton class. So you call a class statically that when first invoked automatically creates an object of itself and stores it. One this happens every static all references the same object and because of this you don't get multiple tooltips or conflicts.

I use ExtJS and they do tooltips, and message boxes as both flyweight elements. I'm hoping that your frameworks had flyweight elements as well, otherwise you will just have to make your own singleton and call it.

查看更多
太酷不给撩
5楼-- · 2019-01-24 14:50

It could be that the display isn't refreshing fast enough. Depending on the JS library you are using, you might be able to put a tiny delay on the pop-up "show" effect.

查看更多
孤傲高冷的网名
6楼-- · 2019-01-24 14:54

Yes, Javascript is single-threaded. Even with browsers like Google Chrome, there is one thread per tab.

Without knowing how you are trying to cancel one pop-up from another, it's hard to say what is the cause of your problem.

If your DIVs are nested within one another, you may have an event propagation issue.

查看更多
迷人小祖宗
7楼-- · 2019-01-24 14:56

Here's the working version, more or less. When creating items we attach a mouseover event:

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

This calls a function that sets a timeout (pre-existing timeouts for a different item is cancelled first):

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

This in turn shows the bubble if it fires before being cancelled:

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

This seems to work now I have set the timeout to 200 ms rather than 100 ms. Not sure why too short a timeout causes the multi-bubble thing to happen, but I guess queuing of window events or something might still be happening while the newly added elements are being laid out.

查看更多
登录 后发表回答