Bri*_*ian 186 javascript jquery
我一直认为JavaScript始终是异步的.但是,我了解到有些情况并非如此(即DOM操作).有什么关于它何时同步以及什么时候它将是异步的?jQuery是否会影响这一点?
cle*_*tus 258
JavaScript始终是同步和单线程的.如果您在页面上执行JavaScript代码块,则当前不会执行该页面上的其他JavaScript.
从某种意义上说,JavaScript只能是异步的,例如,它可以进行Ajax调用.Ajax调用将停止执行,其他代码将能够执行,直到调用返回(成功或否则),此时回调将同步运行.此时不会运行其他代码.它不会中断当前正在运行的任何其他代码.
JavaScript定时器使用相同类型的回调进行操作.
将JavaScript描述为异步可能会产生误导.更准确地说,JavaScript是同步的,并且具有各种回调机制的单线程.
jQuery有一个关于Ajax调用的选项,可以使它们同步(使用该async: false选项).初学者可能会错误地使用它,因为它允许更传统的编程模型,人们可能更习惯.它有问题的原因是该选项将阻止页面上的所有 JavaScript,直到它完成,包括所有事件处理程序和计时器.
小智 193
JavaScript是单线程的,并且具有同步执行模型.单线程意味着一次执行一个命令.同步意味着一次一个,即一行代码正在执行,以便代码出现.所以在JavaScript中,有一件事情一次发生.
执行上下文
JavaScript引擎与浏览器中的其他引擎交互.在JavaScript执行堆栈中,底部有全局上下文,然后当我们调用函数时,JavaScript引擎为各个函数创建新的执行上下文.当被调用函数退出其执行上下文时,将从堆栈中弹出,然后弹出下一个执行上下文,依此类推......
例如
function abc()
{
   console.log('abc');
}
function xyz()
{
   abc()
   console.log('xyz');
}
var one = 1;
xyz();
在上面的代码中,将创建一个全局执行上下文,并在此上下文var one中存储它的值,它的值将为1 ...当调用xyz()调用时,将创建一个新的执行上下文,如果我们定义了任何变量在xyz函数中,这些变量将存储在xyz()的执行上下文中.在xyz函数中,我们调用abc()然后创建abc()执行上下文并将其放在执行堆栈上......现在当abc()完成时,它的上下文从堆栈中弹出,然后从中弹出xyz()上下文堆栈然后全局上下文将被弹出...
现在关于异步回调; 异步意味着一次不止一个.
就像执行堆栈一样,有事件队列.当我们想要通知JavaScript引擎中的某些事件时,我们可以监听该事件,并将该事件放在队列中.例如,Ajax请求事件或HTTP请求事件.
每当执行堆栈为空时(如上面的代码示例所示),JavaScript引擎会定期查看事件队列并查看是否有任何要通知的事件.例如,在队列中有两个事件,ajax请求和HTTP请求.它还会查看是否存在需要在该事件触发器上运行的函数...因此,JavaScript引擎会收到有关该事件的通知,并知道要对该事件执行的相应函数...因此,JavaScript引擎调用处理函数,在示例情况下,例如AjaxHandler()将被调用,就像调用函数时一样,它的执行上下文放在执行上下文中,现在函数执行完成,事件ajax请求也从事件队列中删除... 当AjaxHandler()完成时,执行堆栈为空,因此引擎再次查看事件队列并运行队列中下一个HTTP请求的事件处理函数.重要的是要记住只有在执行堆栈为空时才处理事件队列.
例如,请参阅下面的代码,解释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');
和
<html>
    <head>
    </head>
    <body>
        <script src="program.js"></script>
    </body>
</html>
现在运行网页并单击页面,然后在控制台上查看输出.输出将是
waitfunction() context will be popped after this line
global context will be emptied after this line
click event handler...
JavaScript引擎正如执行上下文部分中所解释的那样同步运行代码,浏览器异步地将事物放入事件队列中.因此,需要很长时间才能完成的功能可以中断事件处理.事件之类的浏览器中发生的事情都是通过JavaScript以这种方式处理的,如果有一个应该运行的监听器,引擎将在执行堆栈为空时运行它.并且事件按照它们发生的顺序进行处理,因此异步部分是关于引擎外部发生的事情,即当外部事件发生时引擎应该做什么.
所以JavaScript始终是同步的.
CMS*_*CMS 98
JavaScript是单线程的,并且始终处理正常的同步代码流执行.
JavaScript可以具有的异步行为的良好示例是事件(用户交互,Ajax请求结果等)和计时器,基本上是可能随时发生的操作.
我建议你看一下下面的文章:
该文章将帮助您了解JavaScript的单线程特性以及定时器如何在内部工作以及异步JavaScript执行的工作方式.
async http://ejohn.org/files/427px-Timers.png
Yeh*_*rtz 15
对于真正了解 JS 工作原理的人来说,这个问题可能看起来很奇怪,但是大多数使用 JS 的人没有如此深刻的洞察力(并且不一定需要它),对他们来说这是一个相当令人困惑的点,我会试着从那个角度回答。
JS 的代码执行方式是同步的。每行仅在完成之前在该行之后运行,并且如果该行在完成之后调用一个函数等...
主要的混淆点来自这样一个事实,即您的浏览器能够告诉 JS 随时执行更多代码(类似于如何从控制台在页面上执行更多 JS 代码)。例如,JS 具有回调函数,其目的是允许 JS 异步执行,以便 JS 的其他部分可以在等待已执行的 JS 函数(即GET调用)返回答案时运行,JS 将继续运行,直到浏览器在那时有一个答案,事件循环(浏览器)将执行调用回调函数的 JS 代码。
由于事件循环(浏览器)可以在任何时候输入更多要执行的 JS,因此 JS 是异步的(导致浏览器输入 JS 代码的主要因素是超时、回调和事件)
我希望这足够清楚,可以对某人有所帮助。
术语“异步”的含义可能略有不同,导致这里的答案看似相互矛盾,但实际上并非如此。维基百科上的异步有这样的定义:
在计算机编程中,异步是指独立于主程序流程的事件的发生以及处理此类事件的方式。这些可能是“外部”事件,例如信号的到达,或由程序发起的与程序执行同时发生的动作,程序不会阻塞以等待结果。
非 JavaScript 代码可以将此类“外部”事件排队到一些 JavaScript 的事件队列中。但就目前的情况而言。
为了在您的脚本中执行一些其他 JavaScript 代码,运行 JavaScript 代码没有外部中断。JavaScript 一段接一个地执行,顺序由每个事件队列中事件的顺序以及这些队列的优先级决定。
例如,您可以绝对确定在执行以下代码段时不会执行其他 JavaScript(在同一脚本中):
let a = [1, 4, 15, 7, 2];
let sum = 0;
for (let i = 0; i < a.length; i++) {
    sum += a[i];
}
换句话说,JavaScript 中没有抢占。无论事件队列中可能有什么,这些事件的处理都必须等到这段代码运行完成。EcmaScript 规范在第 8.4 节作业和作业队列中说:
只有当没有正在运行的执行上下文且执行上下文堆栈为空时,才能启动 Job 的执行。
正如其他人已经写过的,在 JavaScript 中有几种情况会出现异步,并且它总是涉及一个事件队列,只有在没有其他 JavaScript 代码执行时才会导致 JavaScript 执行:
setTimeout():当超时到期时,代理(例如浏览器)会将事件放入事件队列中。时间的监控和事件在队列中的放置由非 JavaScript 代码进行,因此您可以想象这与某些 JavaScript 代码的潜在执行并行发生。但是提供给的回调setTimeout只能在当前正在执行的 JavaScript 代码运行完成并且正在读取适当的事件队列时执行。
fetch():代理将使用操作系统功能来执行 HTTP 请求并监视任何传入的响应。同样,这个非 JavaScript 任务可能与一些仍在执行的 JavaScript 代码并行运行。但是,将解析 返回的承诺的承诺解析过程fetch()只能在当前执行的 JavaScript 运行完成时执行。
requestAnimationFrame():当浏览器的渲染引擎(非 JavaScript)准备好执行绘制操作时,它会在 JavaScript 队列中放置一个事件。当处理 JavaScript 事件时,将执行回调函数。
queueMicrotask(): 立即在微任务队列中放置一个事件。当调用堆栈为空并且该事件被消耗时,将执行回调。
还有更多的例子,但所有这些功能都是由宿主环境提供的,而不是由核心 EcmaScript 提供的。使用核心 EcmaScript,您可以使用Promise.resolve().
EcmaScript 提供了多种语言结构来支持异步模式,例如yield、async、await。但请不要搞错:任何 JavaScript 代码都不会被外部事件中断。的“中断”的是yield和await似乎提供仅仅是一个控制,从函数调用返回后来就恢复其执行上下文的预定义的方式,或者通过JS代码(在的情况下yield),或在事件队列(在的情况下await)。
当 JavaScript 代码访问 DOM API 时,这在某些情况下可能会使 DOM API 触发一个或多个同步通知。如果你的代码有一个监听它的事件处理程序,它就会被调用。
这可能会被认为是先发制人的并发,但事实并非如此:一旦您的事件处理程序返回,DOM API 最终也会返回,并且原始 JavaScript 代码将继续。
在其他情况下,DOM API 只会在适当的事件队列中分派一个事件,一旦调用堆栈被清空,JavaScript 就会接收它。
查看同步和异步事件