@@ -13,41 +13,48 @@ while(--i) { console.log(i); }
1313console .log (" while 执行完毕我才能执行" );
1414```
1515### 异步
16- 异步执行就是非阻塞模式执行,每一个任务有一个或多个回调函数` callback ` ,前一个任务结束后,不是执行后一个任务,而是执行回调函数,后一个任务则是不等前一个任务结束就执行,所以程序的执行顺序与任务的排列顺序是不一致的、异步的。浏览器对于每个` Tab ` 只分配了一个` Js ` 线程,主要任务是与用户交互以及操作` DOM ` 等,而这也就决定它只能为单线程,否则会带来很复杂的同步问题,例如假定` JavaScript ` 同时有两个线程,一个线程在某个` DOM ` 节点上添加内容,另一个线程删除了这个节点,这时浏览器无法确定以哪个线程的操作为准。
16+ 异步执行就是非阻塞模式执行,每一个任务有一个或多个回调函数` callback ` ,前一个任务结束后,不是执行后一个任务,而是执行回调函数,后一个任务则是不等前一个任务结束就执行,所以程序的执行顺序与任务的排列顺序是不一致的、异步的。浏览器对于每个` Tab ` 只分配了一个` Js ` 线程,主要任务是与用户交互以及操作` DOM ` 等,而这也就决定它只能为单线程,否则会带来很复杂的同步问题。例如假定` JavaScript ` 同时有两个线程,一个线程在某个` DOM ` 节点上添加内容,另一个线程删除了这个节点,这时浏览器无法确定以哪个线程的操作为准。
17+
18+ 在下面的示例中需要注意的是,` W3C ` 在` HTML ` 标准中规定,规定要求` setTimeout ` 中低于` 4ms ` 的时间间隔算为` 4ms ` 。此外,这与浏览器设定、主线程以及任务队列也有关系,执行时间可能大于` 4ms ` ,例如老版本的浏览器都将最短间隔设为` 10 ` 毫秒。对于那些` DOM ` 的变动尤其是涉及页面重新渲染的部分,通常不会立即执行,而是每` 16 ` 毫秒执行一次,这时使用` requestAnimationFrame() ` 的效果要好于` setTimeout() ` 。
1719
1820``` javascript
1921setTimeout (() => console .log (" 我后执行" ), 0 );
20- // 注意:W3C在HTML标准中规定,规定要求setTimeout中低于4ms的时间间隔算为4ms,此外这与浏览器设定、主线程以及任务队列也有关系,执行时间可能大于4ms,例如老版本的浏览器都将最短间隔设为10毫秒。另外,对于那些DOM的变动尤其是涉及页面重新渲染的部分,通常不会立即执行,而是每16毫秒执行一次。这时使用requestAnimationFrame()的效果要好于setTimeout()。
2122console .log (" 我先执行" );
2223```
2324
2425## 异步机制
25- 首先来看一个例子,与上文一样来测试一个异步执行的操作
26+ 首先来看一个例子,与上文一样来测试一个异步执行的操作。
27+
2628``` javascript
2729setTimeout (() => console .log (" 我在很长时间之后才执行" ), 0 );
2830var i = 3000000000 ;
2931while (-- i) { }
3032console .log (" 循环执行完毕" );
3133```
32- 本地测试,设置的` setTimeout ` 回调函数大约在` 30s ` 之后才执行,远远大于` 4ms ` ,我在主线程设置了一个非常大的循环来阻塞` Js ` 主线程,注意我并没有设置一个死循环,假如我在此处设置死循环来阻塞主线程,那么设置的` setTimeout ` 回调函数将永远不会执行,此外由于渲染线程与` JS ` 引擎线程是互斥的,` Js ` 线程在处理任务时渲染线程会被挂起,整个页面都将被阻塞,无法刷新甚至无法关闭,只能通过使用任务管理器结束` Tab ` 进程的方式关闭页面。
34+
35+ 本地测试,设置的` setTimeout ` 回调函数大约在` 30s ` 之后才执行,远远大于` 4ms ` ,我在主线程设置了一个非常大的循环来阻塞` Js ` 主线程,注意我并没有设置一个死循环,假如我在此处设置死循环来阻塞主线程,那么设置的` setTimeout ` 回调函数将永远不会执行。此外由于渲染线程与` JS ` 引擎线程是互斥的,` Js ` 线程在处理任务时渲染线程会被挂起,整个页面都将被阻塞,无法刷新甚至无法关闭,只能通过使用任务管理器结束` Tab ` 进程的方式关闭页面。
36+
3337` Js ` 实现异步是通过一个执行栈与一个任务队列来完成异步操作的,所有同步任务都是在主线程上执行的,形成执行栈,任务队列中存放各种事件回调(也可以称作消息),当执行栈中的任务处理完成后,主线程就开始读取任务队列中的任务并执行,不断往复循环。
34- 例如上例中的` setTimeout ` 完成后的事件回调就存在任务队列中,这里需要说明的是浏览器定时计数器并不是由` JavaScript ` 引擎计数的,因为` JavaScript ` 引擎是单线程的,如果线程处于阻塞状态就会影响记计时的准确,计数是由浏览器线程进行计数的,当计数完毕,就将事件回调加入任务队列,同样` HTTP ` 请求在浏览器中也存在单独的线程,也是执行完毕后将事件回调置入任务队列。通过这个流程,就能够解释为什么上例中` setTimeout ` 的回调一直无法执行,是由于主线程也就是执行栈中的代码没有完成,不会去读取任务队列中的事件回调来执行,即使这个事件回调早已在任务队列中。
38+
39+ 例如上例中的` setTimeout ` 完成后的事件回调就存在任务队列中,这里需要说明的是浏览器定时计数器并不是由` JavaScript ` 引擎计数的。因为` JavaScript ` 引擎是单线程的,如果线程处于阻塞状态就会影响记计时的准确,计数是由浏览器线程进行计数的,当计数完毕,就将事件回调加入任务队列。同样` HTTP ` 请求在浏览器中也存在单独的线程,也是执行完毕后将事件回调置入任务队列。通过这个流程,就能够解释为什么上例中` setTimeout ` 的回调一直无法执行,是由于主线程也就是执行栈中的代码没有完成,不会去读取任务队列中的事件回调来执行,即使这个事件回调早已在任务队列中。
3540
3641### Event Loop
37- 主线程从任务队列中读取事件,这个过程是循环不断的,所以整个的这种运行机制又称为` Event Loop ` ,` Event Loop ` 是一个执行模型,在不同的地方有不同的实现,浏览器和` NodeJS ` 基于不同的技术实现了各自的` Event Loop ` 。浏览器的` Event Loop ` 是在` HTML5 ` 的规范中明确定义,` NodeJS ` 的` Event Loop ` 是基于` libuv ` 实现的。
42+ 主线程从任务队列中读取事件,这个过程是循环不断的,所以整个的这种运行机制又称为` Event Loop ` ,` Event Loop ` 是一个执行模型,在不同的地方有不同的实现,浏览器和` NodeJS ` 基于不同的技术实现了各自的` Event Loop ` 。浏览器的` Event Loop ` 是在` HTML5 ` 的规范中明确定义,` NodeJS ` 的` Event Loop ` 是基于` libuv ` 实现的。
43+
3844在浏览器中的` Event Loop ` 由执行栈` Execution Stack ` 、后台线程` Background Threads ` 、宏队列` Macrotask Queue ` 、微队列` Microtask Queue ` 组成。
3945
4046* 执行栈就是在主线程执行同步任务的数据结构,函数调用形成了一个由若干帧组成的栈。
4147* 后台线程就是浏览器实现对于` setTimeout ` 、` setInterval ` 、` XMLHttpRequest ` 等等的执行线程。
42- * 宏队列,一些异步任务的回调会依次进入宏队列,等待后续被调用,包括` setTimeout ` 、` setInterval ` 、` setImmediate(Node) ` 、` requestAnimationFrame ` 、` UI rendering ` 、` I/O ` 等操作
43- * 微队列,另一些异步任务的回调会依次进入微队列,等待后续调用,包括` Promise ` 、` process.nextTick(Node) ` 、` Object.observe ` 、` MutationObserver ` 等操作
44-
45- 当` Js ` 执行时,进行如下流程
46- 1 . 首先将执行栈中代码同步执行,将这些代码中异步任务加入后台线程中
47- 2 . 执行栈中的同步代码执行完毕后,执行栈清空,并开始扫描微队列
48- 3 . 取出微队列队首任务,放入执行栈中执行,此时微队列是进行了出队操作
49- 4 . 当执行栈执行完成后,继续出队微队列任务并执行,直到微队列任务全部执行完毕
50- 5 . 最后一个微队列任务出队并进入执行栈后微队列中任务为空,当执行栈任务完成后,开始扫面微队列为空,继续扫描宏队列任务,宏队列出队,放入执行栈中执行,执行完毕后继续扫描微队列为空则扫描宏队列,出队执行
48+ * 宏队列,一些异步任务的回调会依次进入宏队列,等待后续被调用,包括` setTimeout ` 、` setInterval ` 、` setImmediate(Node) ` 、` requestAnimationFrame ` 、` UI rendering ` 、` I/O ` 等操作。
49+ * 微队列,另一些异步任务的回调会依次进入微队列,等待后续调用,包括` Promise ` 、` process.nextTick(Node) ` 、` Object.observe ` 、` MutationObserver ` 等操作。
50+
51+ 当` Js ` 执行时,进行如下流程:
52+
53+ 1 . 首先将执行栈中代码同步执行,将这些代码中异步任务加入后台线程中。
54+ 2 . 执行栈中的同步代码执行完毕后,执行栈清空,并开始扫描微队列。
55+ 3 . 取出微队列队首任务,放入执行栈中执行,此时微队列是进行了出队操作。
56+ 4 . 当执行栈执行完成后,继续出队微队列任务并执行,直到微队列任务全部执行完毕。
57+ 5 . 最后一个微队列任务出队并进入执行栈后微队列中任务为空,当执行栈任务完成后,开始扫面微队列为空,继续扫描宏队列任务,宏队列出队,放入执行栈中执行,执行完毕后继续扫描微队列为空则扫描宏队列,出队执行。
51586 . 不断往复...
5259
5360#### 实例
@@ -95,14 +102,16 @@ console.log(7);
95102*/
96103```
97104
98- ##### Step 1
105+ #### Step 1
106+
99107``` javascript
100108// 执行栈 console
101109// 微队列 []
102110// 宏队列 []
103111console .log (1 ); // 1
104112```
105- ##### Step 2
113+ #### Step 2
114+
106115``` javascript
107116// 执行栈 setTimeout
108117// 微队列 []
@@ -114,7 +123,8 @@ setTimeout(() => {
114123 });
115124}, 0 );
116125```
117- ##### Step 3
126+ #### Step 3
127+
118128``` javascript
119129// 执行栈 Promise
120130// 微队列 [then1]
@@ -127,7 +137,8 @@ new Promise((resolve, reject) => {
127137})
128138```
129139
130- ##### Step 4
140+ #### Step 4
141+
131142``` javascript
132143// 执行栈 setTimeout
133144// 微队列 [then1]
@@ -137,23 +148,26 @@ setTimeout(() => {
137148}, 0 );
138149```
139150
140- ##### Step 5
151+ #### Step 5
152+
141153``` javascript
142154// 执行栈 console
143155// 微队列 [then1]
144156// 宏队列 [setTimeout1 setTimeout2]
145157console .log (7 ); // 7
146158```
147159
148- ##### Step 6
160+ #### Step 6
161+
149162``` javascript
150163// 执行栈 then1
151164// 微队列 []
152165// 宏队列 [setTimeout1 setTimeout2]
153166console .log (5 ); // 5
154167```
155168
156- ##### Step 7
169+ #### Step 7
170+
157171``` javascript
158172// 执行栈 setTimeout1
159173// 微队列 [then2]
@@ -164,15 +178,17 @@ Promise.resolve().then(() => {
164178});
165179```
166180
167- ##### Step 8
181+ #### Step 8
182+
168183``` javascript
169184// 执行栈 then2
170185// 微队列 []
171186// 宏队列 [setTimeout2]
172187console .log (3 ); // 3
173188```
174189
175- ##### Step 9
190+ #### Step 9
191+
176192``` javascript
177193// 执行栈 setTimeout2
178194// 微队列 []
@@ -182,18 +198,14 @@ console.log(6); // 6
182198
183199## 每日一题
184200
185- ```
186- https://github.com/WindrunnerMax/EveryDay
187- ```
201+ - < https://github.com/WindRunnerMax/EveryDay >
188202
189203
190204## 参考
191205
192- ```
193- https://www.jianshu.com/p/1a35857c78e5
194- https://segmentfault.com/a/1190000016278115
195- https://segmentfault.com/a/1190000012925872
196- https://www.cnblogs.com/sunidol/p/11301808.html
197- http://www.ruanyifeng.com/blog/2014/10/event-loop.html
198- https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/EventLoop
199- ```
206+ - < https://www.jianshu.com/p/1a35857c78e5 >
207+ - < https://segmentfault.com/a/1190000016278115 >
208+ - < https://segmentfault.com/a/1190000012925872 >
209+ - < https://www.cnblogs.com/sunidol/p/11301808.html >
210+ - < http://www.ruanyifeng.com/blog/2014/10/event-loop.html >
211+ - < https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/EventLoop >
0 commit comments