异步与事件队列的初步介绍.

理解同步和异步

同步: 当你在玩游戏时,肚子突然饿了,于是花了20分钟自己做了一个披萨.之后开始吃然后继续玩游戏.

异步: 当你在玩游戏时,肚子突然饿了,于是拿起手机点了一份20元的披萨.之后开始吃然后继续玩游戏.

首先明确,你的主要目的是完成两件事,玩游戏,吃披萨.

在同步的事件中,你用了20分钟玩游戏的时间自己做了一个披萨,吃到了披萨.
在异步的事件中,你花费了20元买了一个披萨,吃到了披萨.

参考来源

JavaScript是单线程,同步的

单线程(single-threaded): 一次只执行一条指令.线程是CPU顺序执行指令的一个调度单位.

JS引擎中的行为是单线程,同步的,但在浏览器中,不仅仅只有JS引擎存在,还存在其他部分(如渲染引擎),JS引擎和外部的交流是异步的.

browser_async

事件队列

JS引擎工作时除了有执行栈,还存在一个事件队列(Event Queue),如果用户触发了JS引擎能够处理的事件,这个事件就会被放置于事件队列中等待JS引擎处理,当执行栈为空时,JS引擎开始处理队列中的事件.只有执行栈空时才会开始处理.JS引擎检查事件队列的持续行为称为 Event Loop .

所以异步指的是浏览器异步将事件置于事件队列,是JS引擎外的行为,代码依旧是一行一行执行.

event_q1
event_q2
event_q3

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
function waitThreeSeconds() {
var ms = 3000 + new Date().getTime();
while (new Date() < ms){}
console.log('finished function');
}

function clickHandler() {
console.log('click event!');
}


document.addEventListener('click', clickHandler);


waitThreeSeconds();
console.log('finished execution');

// finished function
// finished execution
// ('click event!')
// 顺序不会改变

console.log('finished execution');这段代码结束执行之后,执行栈才空,开始处理点击事件.

Ps: js文件的位置

app.js必须放在body的关闭标签之前,如果放在<head>标签之内,三秒之内无论点击多少次,click事件都不会执行.

1
2
3
4
5
6
7
8
9
10
11
12
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
<!-- Don't do this: -->
<!-- <script src='/app.js'></script> -->
</head>
<body>
<script src='/app.js'></script>
</body>
</html>

原因:

浏览器加载含有<script>标签的网页过程:

  1. 获取HTML
  2. 开始解析HTML
  3. 解析器解析到连接外部JS文件的<script>标签
  4. 浏览器请求外部JS文件,同时暂停解析HTML
  5. 一段时间后JS文件加载完成然后成功执行
  6. 之后解析器开始解析余下部分的HTML

因此如果script在head标签内,表示浏览器的页面还没有渲染,无论如何点击,都获取不到这一事件.