JavaScript 什么时候同步?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/2035645/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
When is JavaScript synchronous?
提问by Brian
I have been under the impression for that JavaScript was always asynchronous. However, I have learned that there are situations where it is not (ie DOM manipulations). Is there a good reference anywhere about when it will be synchronous and when it will be asynchronous? Does jQuery affect this at all?
我一直认为 JavaScript 总是异步的。但是,我了解到有些情况并非如此(即 DOM 操作)。是否有关于何时同步和何时异步的良好参考?jQuery 会影响这个吗?
回答by cletus
JavaScript is always synchronous and single-threaded. If you're executing a JavaScript block of code on a page then no other JavaScript on that page will currently be executed.
JavaScript 始终是同步的和单线程的。如果您在页面上执行 JavaScript 代码块,则当前不会执行该页面上的其他 JavaScript。
JavaScript is only asynchronous in the sense that it can make, for example, Ajax calls. The Ajax call will stop executing and other code will be able to execute until the call returns (successfully or otherwise), at which point the callback will run synchronously. No other code will be running at this point. It won't interrupt any other code that's currently running.
JavaScript 只是在它可以进行例如 Ajax 调用的意义上是异步的。Ajax 调用将停止执行,其他代码将能够执行,直到调用返回(成功或失败),此时回调将同步运行。此时不会运行其他代码。它不会中断当前正在运行的任何其他代码。
JavaScript timers operate with this same kind of callback.
JavaScript 计时器使用相同类型的回调进行操作。
Describing JavaScript as asynchronous is perhaps misleading. It's more accurate to say that JavaScript is synchronous and single-threaded with various callback mechanisms.
将 JavaScript 描述为异步可能会产生误导。更准确地说,JavaScript 是同步的、单线程的,具有各种回调机制。
jQuery has an option on Ajax calls to make them synchronously (with the async: falseoption). Beginners might be tempted to use this incorrectly because it allows a more traditional programming model that one might be more used to. The reason it's problematic is that this option will block allJavaScript on the page until it finishes, including all event handlers and timers.
jQuery 有一个关于 Ajax 调用的选项,可以使它们同步(使用该async: false选项)。初学者可能会倾向于错误地使用它,因为它允许使用更传统的编程模型,而人们可能更习惯使用它。有问题的原因是该选项将阻止页面上的所有JavaScript,直到它完成,包括所有事件处理程序和计时器。
回答by cletus
JavaScript is single threaded and has a synchronous execution model. Single threaded means that one command is being executed at a time. Synchronous means one at a time i.e. one line of code is being executed at time in order the code appears. So in JavaScript one thing is happening at a time.
JavaScript 是单线程的并且具有同步执行模型。单线程意味着一次执行一个命令。同步意味着一次一个,即一行代码按代码出现的顺序执行。所以在 JavaScript 中,一次发生一件事。
Execution Context
执行上下文
The JavaScript engine interacts with other engines in the browser. In the JavaScript execution stack there is global context at the bottom and then when we invoke functions the JavaScript engine creates new execution contexts for respective functions. When the called function exits its execution context is popped from the stack, and then next execution context is popped and so on...
JavaScript 引擎与浏览器中的其他引擎交互。在 JavaScript 执行堆栈底部有全局上下文,然后当我们调用函数时,JavaScript 引擎为各个函数创建新的执行上下文。当被调用的函数退出时,它的执行上下文从堆栈中弹出,然后下一个执行上下文被弹出,依此类推……
For example
例如
function abc()
{
console.log('abc');
}
function xyz()
{
abc()
console.log('xyz');
}
var one = 1;
xyz();
In the above code a global execution context will be created and in this context var onewill be stored and its value will be 1... when the xyz() invocation is called then a new execution context will be created and if we had defined any variable in xyz function those variables would be stored in the execution context of xyz(). In the xyz function we invoke abc() and then the abc() execution context is created and put on the execution stack... Now when abc() finishes its context is popped from stack, then the xyz() context is popped from stack and then global context will be popped...
在上面的代码中,将创建一个全局执行上下文,并在此上下文var one中存储其值,其值为 1...当调用 xyz() 时,将创建一个新的执行上下文,如果我们定义了任何变量在 xyz 函数中,这些变量将存储在 xyz() 的执行上下文中。在 xyz 函数中,我们调用 abc() 然后创建 abc() 执行上下文并将其放入执行堆栈...现在当 abc() 完成它的上下文从堆栈中弹出,然后 xyz() 上下文从中弹出堆栈然后将弹出全局上下文...
Now about asynchronous callbacks; asynchronous means more than one at a time.
现在关于异步回调;异步意味着一次多个。
Just like the execution stack there is the Event Queue. When we want to be notified about some event in the JavaScript engine we can listen to that event, and that event is placed on the queue. For example an Ajax request event, or HTTP request event.
就像执行堆栈一样,有Event Queue。当我们想要在 JavaScript 引擎中收到有关某个事件的通知时,我们可以侦听该事件,并将该事件放置在队列中。例如 Ajax 请求事件或 HTTP 请求事件。
Whenever the execution stack is empty, like shown in above code example, the JavaScript engine periodically looks at the event queue and sees if there is any event to be notified about. For example in the queue there were two events, an ajax request and a HTTP request. It also looks to see if there is a function which needs to be run on that event trigger... So the JavaScript engine is notified about the event and knows the respective function to execute on that event... So the JavaScript engine invokes the handler function, in the example case, e.g. AjaxHandler() will be invoked and like always when a function is invoked its execution context is placed on the execution context and now the function execution finishes and the event ajax request is also removed from the event queue... When AjaxHandler() finishes the execution stack is empty so the engine again looks at the event queue and runs the event handler function of HTTP request which was next in queue. It is important to remember that the event queue is processed only when execution stack is empty.
每当执行堆栈为空时,如上面的代码示例所示,JavaScript 引擎会定期查看事件队列并查看是否有任何要通知的事件。例如在队列中有两个事件,一个 ajax 请求和一个 HTTP 请求。它还查看是否有需要在该事件触发器上运行的函数……因此 JavaScript 引擎会收到有关该事件的通知,并知道要在该事件上执行的相应函数……因此 JavaScript 引擎调用处理程序函数,在示例情况下,例如 AjaxHandler() 将被调用,并且总是在调用函数时将其执行上下文放在执行上下文中,现在函数执行完成并且事件 ajax 请求也从事件队列中删除... 当 AjaxHandler() 完成时,执行堆栈为空,因此引擎再次查看事件队列并运行队列中下一个 HTTP 请求的事件处理函数。重要的是要记住,只有在执行堆栈为空时才处理事件队列。
For example see the code below explaining the execution stack and event queue handling by Javascript engine.
例如,请参阅下面的代码,解释 Javascript 引擎对执行堆栈和事件队列的处理。
function waitfunction() {
var a = 5000 + new Date().getTime();
while (new Date() < a){}
console.log('waitfunction() context will be popped after this line');
}
function clickHandler() {
console.log('click event handler...');
}
document.addEventListener('click', clickHandler);
waitfunction(); //a new context for this function is created and placed on the execution stack
console.log('global context will be popped after this line');
And
和
<html>
<head>
</head>
<body>
<script src="program.js"></script>
</body>
</html>
Now run the webpage and click on the page, and see the output on console. The output will be
现在运行网页并单击该页面,然后在控制台上查看输出。输出将是
waitfunction() context will be popped after this line
global context will be emptied after this line
click event handler...
The JavaScript engine is running the code synchronously as explained in the execution context portion, the browser is asynchronously putting things in event queue. So the functions which take a very long time to complete can interrupt event handling. Things happening in a browser like events are handled this way by JavaScript, if there is a listener supposed to run, the engine will run it when the execution stack is empty. And events are processed in the order they happen, so the asynchronous part is about what is happening outside the engine i.e. what should the engine do when those outside events happen.
JavaScript 引擎正在同步运行代码,如执行上下文部分所述,浏览器将事物异步放入事件队列中。所以需要很长时间才能完成的功能可以中断事件处理。JavaScript 以这种方式处理浏览器中发生的诸如事件之类的事情,如果应该运行一个侦听器,则引擎将在执行堆栈为空时运行它。并且事件是按照它们发生的顺序处理的,所以异步部分是关于引擎外部发生的事情,即当这些外部事件发生时引擎应该做什么。
So JavaScript is always synchronous.
所以 JavaScript 总是同步的。
回答by CMS
JavaScript is single-threaded, and all the time you work on a normal synchronous code-flow execution.
JavaScript 是单线程的,并且您一直在处理正常的同步代码流执行。
Good examples of the asynchronous behavior that JavaScript can have are events (user interaction, Ajax request results, etc) and timers, basically actions that might happen at any time.
JavaScript 可以具有的异步行为的好例子是事件(用户交互、Ajax 请求结果等)和计时器,基本上是可能随时发生的动作。
I would recommend you to give a look to the following article:
我建议你看看下面的文章:
That article will help you to understand the single-threaded nature of JavaScript and how timers work internally and how asynchronous JavaScript execution works.
那篇文章将帮助您了解 JavaScript 的单线程特性以及计时器在内部如何工作以及异步 JavaScript 执行如何工作。
回答by Yehuda Schwartz
To someone who really understands how JS works this question might seem off, however most people who use JS do not have such a deep level of insight (and don't necessarily need it) and to them this is a fairly confusing point, I will try to answer from that perspective.
对于真正了解 JS 工作原理的人来说,这个问题可能看起来很奇怪,但是大多数使用 JS 的人并没有如此深刻的洞察力(并且不一定需要它),对他们来说这是一个相当令人困惑的点,我会试着从那个角度回答。
JS is synchronous in the way its code is executed. each line only runs after the line before it has completed and if that line calls a function after that is complete ect...
JS 的代码执行方式是同步的。每行仅在完成之前在该行之后运行,并且如果该行在完成后调用函数等...
The main point of confusion arises from the fact that your browser is able to tell JS to excute more code at anytime (simmlar to how you can excute more JS code on a page from the console). As an example JS has Callback functions who's purpose is to allow JS to BEHAVE asynchronously so further parts of JS can run while waiting for a JS function that has been executed (I.E. a GETcall) to return back an answer, JS will continue to run until the browser has an answer at that point the event loop (browser) will execute the JS code that calls the callback function.
主要的混淆点来自这样一个事实,即您的浏览器能够随时告诉 JS 执行更多代码(类似于如何从控制台在页面上执行更多 JS 代码)。例如,JS 具有回调函数,其目的是允许 JS 异步执行,以便 JS 的其他部分可以在等待已执行的 JS 函数(即GET调用)返回答案时运行,JS 将继续运行,直到浏览器在那时有一个答案,事件循环(浏览器)将执行调用回调函数的 JS 代码。
Since the event loop (browser) can input more JS to be executed at any point in that sense JS is asynchronous (the primary things that will cause a browser to input JS code are timeouts, callbacks and events)
由于事件循环(浏览器)可以输入更多的 JS 以在任何时候执行,因此 JS 是异步的(导致浏览器输入 JS 代码的主要因素是超时、回调和事件)
I hope this is clear enough to be helpful to somebody.
我希望这足够清楚,可以对某人有所帮助。
回答by trincot
Definition
定义
The term "asynchronous" can be used in slightly different meanings, resulting in seemingly conflicting answers here, while they are actually not. Wikipedia on Asynchronyhas this definition:
术语“异步”的含义可能略有不同,导致这里的答案看似相互矛盾,但实际上并非如此。维基百科上的异步有这样的定义:
Asynchrony, in computer programming, refers to the occurrence of events independent of the main program flow and ways to deal with such events. These may be "outside" events such as the arrival of signals, or actions instigated by a program that take place concurrently with program execution, without the program blocking to wait for results.
在计算机编程中,异步是指独立于主程序流程的事件的发生以及处理此类事件的方式。这些可能是“外部”事件,例如信号的到达,或由程序发起的与程序执行同时发生的动作,程序不会阻塞以等待结果。
non-JavaScript code can queue such "outside" events to some of JavaScript's event queues. But that is as far as it goes.
非 JavaScript 代码可以将此类“外部”事件排队到一些 JavaScript 的事件队列中。但就目前的情况而言。
No Preemption
无抢占
There is no external interruptionof running JavaScript code in order to execute some other JavaScript code in your script. Pieces of JavaScript are executed one after the other, and the order is determined by the order of events in each event queue, and the priority of those queues.
为了在您的脚本中执行一些其他 JavaScript 代码,运行 JavaScript 代码没有外部中断。JavaScript 一段接一个地执行,顺序由每个事件队列中事件的顺序以及这些队列的优先级决定。
For instance, you can be absolutely sure that no other JavaScript (in the same script) will ever execute while the following piece of code is executing:
例如,您可以绝对确定在执行以下代码时不会执行其他 JavaScript(在同一脚本中):
let a = [1, 4, 15, 7, 2];
let sum = 0;
for (let i = 0; i < a.length; i++) {
sum += a[i];
}
In other words, there is no preemptionin JavaScript. Whatever may be in the event queues, the processing of those events will have to wait until such piece of code has ran to completion. The EcmaScript specification says in section 8.4 Jobs and Jobs Queues:
换句话说,JavaScript 中没有抢占。无论事件队列中可能有什么,这些事件的处理都必须等到这段代码运行完成。EcmaScript 规范在第 8.4 节作业和作业队列中说:
Execution of a Job can be initiated only when there is no running execution context and the execution context stack is empty.
只有当没有正在运行的执行上下文且执行上下文堆栈为空时,才能启动 Job 的执行。
Examples of Asynchrony
异步示例
As others have already written, there are several situations where asynchrony comes into play in JavaScript, and it always involves an event queue, which can only result in JavaScript execution when there is no other JavaScript code executing:
正如其他人已经写过的,在 JavaScript 中有几种情况会发生异步,并且它总是涉及一个事件队列,只有在没有其他 JavaScript 代码执行时才会导致 JavaScript 执行:
setTimeout(): the agent (e.g. browser) will put an event in an event queue when the timeout has expired. The monitoring of the time and the placing of the event in the queue happens by non-JavaScript code, and so you could imagine this happens in parallel with the potential execution of some JavaScript code. But the callback provided tosetTimeoutcan only execute when the currently executing JavaScript code has ran to completion and the appropriate event queue is being read.fetch(): the agent will use OS functions to perform an HTTP request and monitor for any incoming response. Again, this non-JavaScript task may run in parallel with some JavaScript code that is still executing. But the promise resolution procedure, that will resolve the promise returned byfetch(), can only execute when the currently executing JavaScript has ran to completion.requestAnimationFrame(): the browser's rendering engine (non-JavaScript) will place an event in the JavaScript queue when it is ready to perform a paint operation. When JavaScript event is processed the callback function is executed.queueMicrotask(): immediately places an event in the microtask queue. The callback will be executed when the call stack is empty and that event is consumed.
setTimeout():当超时到期时,代理(例如浏览器)会将事件放入事件队列中。时间的监控和事件在队列中的放置由非 JavaScript 代码进行,因此您可以想象这与某些 JavaScript 代码的潜在执行并行发生。但是提供给的回调setTimeout只能在当前正在执行的 JavaScript 代码运行完成并且正在读取适当的事件队列时执行。fetch():代理将使用操作系统功能来执行 HTTP 请求并监视任何传入的响应。同样,这个非 JavaScript 任务可能与一些仍在执行的 JavaScript 代码并行运行。但是,将解析 返回的承诺的承诺解析过程fetch()只能在当前执行的 JavaScript 运行完成时执行。requestAnimationFrame():当浏览器的渲染引擎(非 JavaScript)准备好执行绘制操作时,它会在 JavaScript 队列中放置一个事件。当处理 JavaScript 事件时,将执行回调函数。queueMicrotask(): 立即在微任务队列中放置一个事件。当调用堆栈为空并且该事件被消耗时,将执行回调。
There are many more examples, but all these functions are provided by the host environment, not by core EcmaScript. With core EcmaScript you can synchronously place an event in a Promise Job Queue with Promise.resolve().
还有更多的例子,但所有这些功能都是由宿主环境提供的,而不是由核心 EcmaScript 提供的。使用核心 EcmaScript,您可以使用Promise.resolve().
Language Constructs
语言结构
EcmaScript provides several language constructs to support the asynchrony pattern, such as yield, async, await. But let there be no mistake: no JavaScript code will be interruptedby an external event. The "interruption" that yieldand awaitseem to provide is just a controlled, predefined way of returning from a function call and restoring its execution context later on, either by JS code (in the case of yield), or the event queue (in the case of await).
EcmaScript 提供了多种语言结构来支持异步模式,例如yield、async、await。但请不要搞错:任何 JavaScript 代码都不会被外部事件中断。的“中断”的是yield和await似乎提供仅仅是一个控制,从函数调用返回后来就恢复其执行上下文的预定义的方式,或者通过JS代码(在的情况下yield),或在事件队列(在的情况下await)。
DOM event handling
DOM 事件处理
When JavaScript code accesses the DOM API, this may in some cases make the DOM API trigger one or more synchronous notifications. And if your code has an event handler listening to that, it will be called.
当 JavaScript 代码访问 DOM API 时,这在某些情况下可能会使 DOM API 触发一个或多个同步通知。如果你的代码有一个监听它的事件处理程序,它就会被调用。
This may come across as pre-emptive concurrency, but it is not: once your event handler(s) return(s), the DOM API will eventually also return, and the original JavaScript code will continue.
这可能会被认为是先发制人的并发,但事实并非如此:一旦您的事件处理程序返回,DOM API 最终也会返回,并且原始 JavaScript 代码将继续。
In other cases the DOM API will just dispatch an event in the appropriate event queue, and JavaScript will pick it up once the call stack has been emptied.
在其他情况下,DOM API 只会在适当的事件队列中分派一个事件,一旦调用堆栈被清空,JavaScript 就会接收它。
See synchronous and asynchronous events
查看同步和异步事件


