直接从两个题目开始吧:
console.log('start');
setTimeout(function() {
console.log('timeout1');
}, 0);
new Promise(resolve => {
console.log('promise1');
resolve();
setTimeout(() => console.log('timeout2'), 0);
}).then(function() {
console.log('then1');
setTimeout(() => console.log('timeout3'), 0);
}).then(function() {
console.log('then2');
setTimeout(() => console.log('timeout4'), 0);
})
console.log('end');
console.log('start');
setTimeout(function() {
console.log('timeout1');
}, 30);
new Promise(resolve => {
console.log('promise1');
resolve();
setTimeout(() => console.log('timeout2'), 20);
}).then(function() {
console.log('then1');
setTimeout(() => console.log('timeout3'), 10);
})
console.log('end');
请写出这两段代码的输出顺序。
如果你可以正确写出答案,并清楚的知道整个Event执行流程的话,就可以跳过该文章了。
这里给出答案:
题1:start promise1 end then1 then2 timeout1 timeout2 timeout3 timeout4
题2:start promise1 end then1 timeout3 timeout2 timeout1
1. 背景
JavaSript是一门单线程语言,所以为了协调事件、用户交互、脚本、UI 渲染和网络处理等行为,用户引擎必须使用event loops。
Event Loop包含两类:一类是基于Browsing Context,一种是基于Worker,二者是独立运行的。本文讲述的是基于Browsing Context的事件循环机制。
2. 相关概念
在JavaScript中,任务可以分为同步任务和异步任务。
同步任务,顾名思义,就是立即执行的任务,同步任务一般会直接进入到主线程中执行;而异步任务,就是异步执行的任务,比如ajax网络请求,setTimeout定时函数等都属于异步任务,异步任务会通过任务队列(Event Queue)的机制来进行协调。
- Execution Stack(主执行栈)
-
Event Queue(任务队列)
-
Macro Task(宏任务)
- setTimeout
- setInterval
- I/O、UI交互事件
- setImmediate(Node.js 环境)
-
Micro Task(微任务)
- Promise
- MutaionObserver
- process.nextTick(Node.js 环境)
-
注:其实这里的表述不严谨,具体见文末
执行过程:同步和异步任务分别进入不同的执行环境
- 同步任务进入主线程,即主执行栈;
- 异步任务进入Event Queue;
- 主线程内的任务执行完毕为空,会去Event Queue读取对应的任务,推入主线程执行;
- 在Event Queue中,需要把所有微任务执行完后再执行宏任务;
上述过程的不断重复就是我们说的Event Loop(事件循环)。
3. 题目解析
在了解了相关概念和执行过程后,我们拿 题目1 具体分析一下。
为了对照方便,我们把题目拷过来:
console.log('start');
setTimeout(function() {
console.log('timeout1');
}, 0);
new Promise(resolve => {
console.log('promise1');
resolve();
setTimeout(() => console.log('timeout2'), 0);
}).then(function() {
console.log('then1');
setTimeout(() => console.log('timeout3'), 0);
}).then(function() {
console.log('then2');
setTimeout(() => console.log('timeout4'), 0);
})
console.log('end');
逐行分析:
- console.log('start') : 同步任务,进入主执行栈
- console.log('timeout1') : 异步任务,并作为宏任务进入Event Queue
- console.log('promise1') : 同步任务,进入主执行栈
- console.log('timeout2') : 异步任务,并作为宏任务进入Event Queue
- console.log('then1') : 异步任务,并作为微任务进入Event Queue
- console.log('timeout3') : 异步任务,并作为宏任务进入Event Quene(该setTimeout虽然在微任务中)
- console.log('then2') : 异步任务,并作为微任务进入Event Queue
- console.log('timeout4') : 异步任务,并作为宏任务进入Event Quene(该setTimeout虽然在微任务中)
- console.log('end') : 同步任务,进入主执行栈
执行顺序:
- 主执行栈先执行:
start promise1 end
- Event Queue微任务执行:
then1 then2
- Event Queue微任务执行:
timeout1 timeout2 timeout3 timeout4
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。