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 stackconsole.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 lineglobal context will be emptied after this line
click event handler...
JavaScript引擎正在同步运行代码,正如在执行上下文部分中所解释的那样,浏览器正在异步地将内容放入事件队列中。因此,需要很长时间才能完成的函数可以中断事件处理。JavaScript以这种方式处理浏览器中发生的事件,如果需要运行侦听器,则当执行堆栈为空时,引擎将运行它。事件是按照它们发生的顺序来处理的,所以异步部分是关于引擎外部正在发生的事情,即当这些外部事件发生时,引擎应该做什么。
所以JavaScript总是同步的。