That's a good question.(这是个好问题。)
I'd love to say “yes”.(我很想说“是”。) I can't.(我不能)
JavaScript is usually considered to have a single thread of execution visible to scripts(*), so that when your inline script, event listener or timeout is entered, you remain completely in control until you return from the end of your block or function.(通常认为JavaScript具有脚本(*)可见的单个执行线程,因此,当您输入内联脚本,事件侦听器或超时时,您将完全处于控制状态,直到从块或函数的结尾返回为止。)
(*: ignoring the question of whether browsers really implement their JS engines using one OS-thread, or whether other limited threads-of-execution are introduced by WebWorkers.)((*:忽略浏览器是否真的使用一个OS线程来实现其JS引擎,或者WebWorkers是否引入了其他有限的执行线程的问题。))
However, in reality this isn't quite true , in sneaky nasty ways.(但是,实际上,这并不是很真实 ,以偷偷摸摸的方式令人讨厌。)
The most common case is immediate events.(最常见的情况是即时事件。)
Browsers will fire these right away when your code does something to cause them:(当您的代码执行某些操作导致它们时,浏览器会立即将其触发:)
var l= document.getElementById('log'); var i= document.getElementById('inp'); i.onblur= function() { l.value+= 'blur\n'; }; setTimeout(function() { l.value+= 'log in\n'; l.focus(); l.value+= 'log out\n'; }, 100); i.focus();
<textarea id="log" rows="20" cols="40"></textarea> <input id="inp">
Results in log in, blur, log out
on all except IE.(log in, blur, log out
除IE以外的所有结果。)
These events don't just fire because you called focus()
directly, they could happen because you called alert()
, or opened a pop-up window, or anything else that moves the focus.(这些事件不仅因为直接调用focus()
而触发,还可能因为调用alert()
或打开弹出窗口或其他任何移动焦点的事件而发生。)
This can also result in other events.(这也可能导致其他事件。)
For example add an i.onchange
listener and type something in the input before the focus()
call unfocuses it, and the log order is log in, change, blur, log out
, except in Opera where it's log in, blur, log out, change
and IE where it's (even less explicably) log in, change, log out, blur
.(例如,添加一个i.onchange
侦听器,然后在focus()
调用将其i.onchange
focus()
之前在输入中键入内容,并且登录顺序为log in, change, blur, log out
,但在Opera中除外,即log in, blur, log out, change
和IE的log in, change, log out, blur
(甚至更不用说了) log in, change, log out, blur
。)
Similarly calling click()
on an element that provides it calls the onclick
handler immediately in all browsers (at least this is consistent!).(同样,在提供该元素的元素上调用click()
在所有浏览器中立即调用onclick
处理程序(至少这是一致的!)。)
(I'm using the direct on...
event handler properties here, but the same happens with addEventListener
and attachEvent
.)((我使用的是直接on...
事件处理程序的属性在这里,但同样的情况addEventListener
和attachEvent
。))
There's also a bunch of circumstances in which events can fire whilst your code is threaded in, despite you having done nothing to provoke it.(在很多情况下,尽管您没有做任何事情来激发代码,但在线程插入代码的过程中仍可能触发事件。)
An example:(一个例子:)
var l= document.getElementById('log'); document.getElementById('act').onclick= function() { l.value+= 'alert in\n'; alert('alert!'); l.value+= 'alert out\n'; }; window.onresize= function() { l.value+= 'resize\n'; };
<textarea id="log" rows="20" cols="40"></textarea> <button id="act">alert</button>
Hit alert
and you'll get a modal dialogue box.(点击alert
,您将看到一个模态对话框。)
No more script executes until you dismiss that dialogue, yes?(在关闭该对话框之前不会执行任何脚本,是吗?) Nope.(不。) Resize the main window and you will get alert in, resize, alert out
in the textarea.(调整主窗口的大小,您将在文本区域中收到alert in, resize, alert out
。)
You might think it's impossible to resize a window whilst a modal dialogue box is up, but not so: in Linux, you can resize the window as much as you like;(您可能会认为在模式对话框打开时无法调整窗口的大小,但事实并非如此:在Linux中,您可以随意调整窗口的大小。)
on Windows it's not so easy, but you can do it by changing the screen resolution from a larger to a smaller one where the window doesn't fit, causing it to get resized.(在Windows上并不是那么容易,但是您可以通过将屏幕分辨率从不适合窗口的较大屏幕分辨率更改为较小屏幕,从而调整屏幕大小来实现。)
You might think, well, it's only resize
(and probably a few more like scroll
) that can fire when the user doesn't have active interaction with the browser because script is threaded.(您可能会认为,当用户由于脚本是线程而无法与浏览器进行有效交互时,仅会触发resize
(可能还会增加诸如scroll
)。)
And for single windows you might be right.(对于单个窗口,您可能是正确的。) But that all goes to pot as soon as you're doing cross-window scripting.(但是,一旦您执行跨窗口脚本编写,所有这些便会付诸东流。) For all browsers other than Safari, which blocks all windows/tabs/frames when any one of them is busy, you can interact with a document from the code of another document, running in a separate thread of execution and causing any related event handlers to fire.(对于Safari以外的所有浏览器,当它们中的任何一个忙时都会阻止所有窗口/选项卡/框架时,您可以与另一个文档的代码中的文档进行交互,在单独的执行线程中运行,并使所有相关的事件处理程序火。)
Places where events that you can cause to be generated can be raised whilst script is still threaded:(在脚本仍然线程化的情况下,可以引发可能引起事件发生的地方:)
when the modal popups ( alert
, confirm
, prompt
) are open, in all browsers but Opera;(当模式弹出窗口( alert
, confirm
, prompt
)打开时,在除Opera外的所有浏览器中;)
during showModalDialog
on browsers that support it;(在支持它的浏览器上的showModalDialog
期间;)
the “A script on this page may be busy...” dialogue box, even if you choose to let the script continue to run, allows events like resize and blur to fire and be handled even whilst the script is in the middle of a busy-loop, except in Opera.(即使您选择让脚本继续运行,“此页面上的脚本可能正忙于...”对话框也会允许调整大小和模糊等事件触发并处理,即使该脚本位于脚本中间。忙循环,Opera除外。)
a while ago for me, in IE with the Sun Java Plugin, calling any method on an applet could allow events to fire and script to be re-entered.(对我来说,前一段时间,在带有Sun Java Plugin的IE中,调用applet上的任何方法都可以触发事件并重新输入脚本。)
This was always a timing-sensitive bug, and it's possible Sun have fixed it since (I certainly hope so).(自始至终,这始终是一个对时间敏感的错误,Sun可能已经对此进行了修复(我当然希望如此)。)
probably more.(可能更多。)
It's been a while since I tested this and browsers have gained complexity since.(自从我测试了这已经有一段时间了,浏览器也因此变得越来越复杂。)
In summary, JavaScript appears to most users, most of the time, to have a strict event-driven single thread of execution.(总之,大多数时候,JavaScript对大多数用户而言似乎具有严格的事件驱动的单线程执行。)
In reality, it has no such thing.(实际上,它没有这样的东西。) It is not clear how much of this is simply a bug and how much deliberate design, but if you're writing complex applications, especially cross-window/frame-scripting ones, there is every chance it could bite you — and in intermittent, hard-to-debug ways.(尚不清楚其中有多少只是一个错误,有多少是经过深思熟虑的设计,但是如果您要编写复杂的应用程序,尤其是跨窗口/框架脚本的应用程序,那么它很有可能会咬住您-并且断断续续地,难以调试的方式。)
If the worst comes to the worst, you can solve concurrency problems by indirecting all event responses.(如果情况变得最糟,则可以通过间接所有事件响应来解决并发问题。)
When an event comes in, drop it in a queue and deal with the queue in order later, in a setInterval
function.(当事件进入时,将其放入队列中,然后在setInterval
函数中按顺序处理该队列。) If you are writing a framework that you intend to be used by complex applications, doing this could be a good move.(如果要编写一个打算由复杂应用程序使用的框架,那么这样做可能是一个不错的选择。) postMessage
will also hopefully soothe the pain of cross-document scripting in the future.(希望将来的postMessage
也能缓解跨文档脚本编写的痛苦。)