reference: https://segmentfault.com/a/1190000016278115
event loop是一个执行模型,在不同的地方有不同的实现。浏览器和NodeJS基于不同的技术实现了各自的Event Loop
浏览器的Event Loop是在html5的规范中明确定义。
NodeJS的Event Loop是基于libuv实现的。可以参考Node的官方文档以及libuv的官方文档。
libuv已经对Event Loop做出了实现,而HTML5规范中只是定义了浏览器中Event Loop的模型,具体的实现留给了浏览器厂商。
1.执行全局script同步代码,这些同步代码有一些是同步语句,有一些是异步语句(比如setTimeout等);
2.全局script代码执行完毕后,调用栈stack会清空;
3.从微队列microtask queue中取出位于队首的回调任务,放入调用栈Stack中执行,执行完后microtask queue长度减1;
4.继续取出位于队首的任务,放入调用栈Stack中执行,以此类推,直到直到把microtask queue中的所有任务都执行完毕。注意,如果在执行microtask的过程中,又产生了microtask,那么会加入到队列的末尾,也会在这个周期被调用执行;
5.microtask queue中的所有任务都执行完毕,此时microtask queue为空队列,调用栈stack也为空;
6.取出宏队列macrotask queue中位于队首的任务,放入Stack中执行;
7.执行完毕后,调用栈stack为空;
8.重复第3-7个步骤;
9.重复第3-7个步骤;
10. ...
1. 宏队列macrotask一次只从队列中取一个任务执行,执行完后就去执行微任务队列中的任务;
2. 微任务队列中所有的任务都会被依次取出来执行,直到microtask queue为空;
3. 图中没有画UI rendering的节点,因为这个是由浏览器自行判断决定的,但是只要执行UI rendering,它的节点是在执行完所有的microtask之后,下一个macrotask之前,紧跟着执行UI render。
事件循环机制:宏任务与微任务、执行栈、执行一个宏任务就清空一次微任务队列,然后再执行宏任务
宏队列,macrotask,也叫tasks
一些异步回调会依次进入macro task queue,等待后续被调用,包括:
setTimeout/setInterval
UI rendering (浏览器独有)
requestAnimationFrame (浏览器独有)
I/O
setImmediate (Node独有)
微队列,microtask,也叫jobs
另一些异步回调会依次进入micro task queue,等待后续被调用,包括:
Promise.then()
Object.observe
MutationObserver
process.nextTick (Node独有)
在使用Promise.resolve(value)等方法的时候,如果promise对象立刻就能进入resolve状态的话,那么你是不是觉得.then里面指定的方法就是同步调用的呢?
实际上,.then中指定的方法调用是异步进行的。
var promise=new Promise(resolve=>{
console.log("inner promise");// 1
resolve(42);
});
promise.then(value=>{
console.log(value);// 3
});
console.log("outer promise");// 2
执行上面的代码会输出下面的log,从这些log我们清楚地知道了上面代码的执行顺序。
inner promise // 1
outer promise // 2
42 // 3
由于JavaScript代码会按照文件的从上到下的顺序执行,所以最开始<1>会执行,然后是resolve(42);被执行。这时候promise对象的已经变为确定状态,FulFilled被设置为了42。
下面的代码promise.then注册了<3>这个回调函数,这是本专栏的焦点问题。
由于promise.then执行的时候promise对象已经是确定状态,从程序上说对回调函数进行同步调用也是行得通的。
但是即使在调用promise.then注册回调函数的时候promise对象已经是确定的状态,Promise也会以异步的方式调用该回调函数,这是在Promise设计上的规定方针。
因此<2>会最先被调用,最后才会调用回调函数<3>。
async functionasync1() {
console.log('async1 start');
await async2();
console.log('async1 end');
}
async functionasync2() {
console.log('async2');
}
console.log('script start');
setTimeout(function() {
console.log('setTimeout1');
}, 200);
setTimeout(function() {
console.log('setTimeout2');
newPromise(function(resolve) {
resolve();
}).then(function() {
console.log('then1')
})
newPromise(function(resolve) {
console.log('Promise1');
resolve();
}).then(function() {
console.log('then2')
})
},0)
async1();
newPromise(function(resolve) {
console.log('promise2');
resolve();
}).then(function() {
console.log('then3');
});
console.log('script end');
// setTimeout是异步任务中的宏任务
setTimeout(() => console.log('a'));
// Promise是异步任务中的微任务,比setTimeout先执行,链式执行法,遇到setTimeout先挂起,往下执行
Promise.resolve().then(
() => console.log('b') // 输出 'b'
).then(
//该函数返回值是 'c'
() => Promise.resolve('c').then(
(data) => {
setTimeout(() => console.log('d')); //挂起,放到宏任务队列之后
console.log('f'); //执行,输出'f'
return data;
})
).then(data => console.log(data) ); // 接收到的就是输出 'c'
// 执行宏任务
setTimeout(() => console.log('a')); //输出 'a'
// 后续添加的宏任务
setTimeout(() => console.log('d')); //输出 'd'
new Promise((resolve) => {
console.log('1')
resolve()
console.log('2')
}).then(() => {
console.log('3')
})
setTimeout(() => {
console.log('4')
})
console.log('5')
由于javascript是单线程任务所以主线程只能同时处理一个任务,所以把异步的事件放在同步的事件处理完成之后再来依次处理。
同步:console.log(1)->console.log(2)->console.log(5);
异步:(微任务)console.log(3)->(宏任务)console.log(4);