JavaScript 是单线程的吗? 如果没有,我如何同步访问共享数据?

发布于 2024-07-06 17:40:45 字数 718 浏览 12 评论 0原文

我有一个带有 DIV 的网页,带有一个 mouseover 处理程序,旨在显示弹出信息气泡。 我不希望一次显示多个信息气泡。 但是,当用户将鼠标快速移动到两个项目上时,我有时会看到两个气泡。 这种情况不应该发生,因为显示弹出窗口的代码取消了之前的弹出窗口。

如果这是一个多线程系统,那么问题就很明显了:有两个线程试图显示弹出窗口,它们都取消现有的弹出窗口,然后弹出自己的弹出窗口。 但我假设 JavaScript 总是单线程运行,这会阻止这种情况发生。 我错了吗? 事件处理程序是否异步运行,在这种情况下我需要同步访问共享数据,或者我应该在库代码中寻找错误以取消弹出窗口?

编辑添加:

  • 有问题的库是 SIMILE Timeline 及其 Ajax 库;
  • 事件处理程序确实调用 SimileAjax.DOM.cancelEvent(domEvt) ,我根据名称假设它会取消事件的冒泡;
  • 只是为了让事情变得更复杂,我实际上正在做的是启动一个超时,如果没有被 moustout 取消,则会显示弹出窗口,这是为了防止弹出窗口烦人地闪烁,但令人烦人的是相反的效果。

我会再戳一下它,看看是否能找出我哪里出错了。 :-)

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. :-)

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(7

写下不归期 2024-07-13 17:40:45

是的,Javascript 是单线程的。 即使使用像 Google Chrome 这样的浏览器,每个选项卡也有一个线程。

如果不知道如何尝试从另一个弹出窗口中取消一个弹出窗口,则很难说出问题的原因是什么。

如果您的 DIV 相互嵌套,则可能会出现一个 事件传播问题。

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.

策马西风 2024-07-13 17:40:45

我不知道您正在使用的库,但如果您只想一次显示某种工具提示......请使用享元对象。 基本上,蝇量级是一次性制造并反复使用的东西。 想想单例类。 因此,您静态调用一个类,该类在首次调用时会自动创建一个自身的对象并存储它。 一种情况是,每个静态都引用同一个对象,因此您不会得到多个工具提示或冲突。

我使用 ExtJS,他们将工具提示和消息框作为蝇量级元素。 我希望你的框架也有享元元素,否则你只需要创建自己的单例并调用它。

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.

囍笑 2024-07-13 17:40:45

它在浏览器中是单线程的。 事件处理程序在一个线程中异步运行,非阻塞并不总是意味着多线程。 你的一个div是另一个div的孩子吗? 因为事件就像 dom 树中的气泡一样从子级传播到父级。

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.

不如归去 2024-07-13 17:40:45

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

当然,这可以通过获取具有相同类的所有元素来进一步概括,迭代它们,然后对它们中的每一个应用相同的事件处理函数。

不管怎样,希望这会有所帮助。

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.

何处潇湘 2024-07-13 17:40:45

仅供参考:从 Firefox 3 开始,有一个与此讨论非常相关的更改:导致同步 XMLHttpRequest 请求的执行线程被分离(这就是接口在同步请求期间不会冻结的原因)并且执行继续。 同步请求完成后,其线程也会继续。 它们不会同时执行,但是依赖于单线程在同步过程(请求)发生时停止的假设不再适用。

FYI: As of Firefox 3 there is a change pretty much relevant to this discussion: execution threads causing synchronous XMLHttpRequest requests get detached (this is why the interface doesn't freeze there during synchronous requests) and the execution continues. Upon synchronous request completion, its thread continues as well. They won't be executed at the same time, however relying on the assumption that single thread stops while a synchronous procedure (request) happening is not applicable any more.

谈情不如逗狗 2024-07-13 17:40:45

可能是显示器刷新速度不够快。 根据您使用的 JS 库,您也许可以对弹出的“显示”效果稍加延迟。

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.

2024-07-13 17:40:45

这是或多或少的工作版本。 创建项目时,我们附加一个 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 毫秒。 不确定为什么超时太短会导致发生多气泡事件,但我猜想在布置新添加的元素时,窗口事件的排队或某些事情可能仍然会发生。

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.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文