事件循环机制
1、事件
JavaScript是一门单线程的,非阻塞的脚本语言
单线程: 任何时候都只有一个主线程(执行栈)处理所有任务
非阻塞:有异步任务时,js会产生一个任务队列,任务队列中的任务不会立即执行,当主线程任务执行完毕,则检查任务队列中是否
有回调,有则压入主线程执行
JS执行机制可以看做一个主线程加上一个任务队列.
同步任务都在主线程(这里的主线程就是JS引擎线程)上执行,会形成一个
执行栈主线程之外,事件触发线程管理着一个
任务队列,只要异步任务有了运行结果,就在任务队列之中放一个事件回调一旦
执行栈中的所有同步任务执行完毕(也就是JS引擎线程空闲了),系统就会读取任务队列,将可运行的异步任务(任务队列中的事件回调,只要任务队列中有事件回调,就说明可以执行) 添加到执行栈中,开始执行。
js执行期间,同步任务都在执行栈中执行,异步任务则被压入任务队列,当同步任务执行完成,则读取任务队列中的任务,当任务队
列中有事件回调,则将其添加到执行栈中开始执行
2、事件循环
取自《深入浅出VueJs》p160,什么是事件循环
处理异步任务时,任务队列有
宏任务队列和微任务队列之分,当执行栈中的任务都执行完毕之后,会去检查微任务队列中是否有事件存在, 如果有,则依次执行微任务队列中事件的回调,直到
执行栈清空;
然后去检查宏任务队列中是否有事件存在,如果有,则依次执行微任务队列中事件的回调,直到执行栈清空;
最后再去检查微任务队列中是否有事件存在,如此循环重复该过程,叫做事件循环
事件循环解释如下:👇👇👇👇👇👇👇👇👇👇👇👇
3、宏任务、微任务
宏任务
我们可以将每次执行栈执行的代码当做是一个宏任务(包括每次从事件队列中获取一个事件回调并放到执行栈中执行), 每一个宏任务会从头到尾执行完毕,不会执行其他
由于
JS引擎线程和GUI渲染线程是互斥的关系,浏览器为了能够使宏任务和DOM任务有序的进行,会在一个宏任务执行结果后,在下一个宏任务执行前,GUI渲染线程开始工作,对页面进行渲染宏任务 --> GUI 渲染 --> 宏任务
常见的宏任务
主代码块
- setTimeout
- setInterval
- setImmediate ()-Node
- requestAnimationFrame ()-浏览器
微任务
我们已经知道
宏任务结束后,会执行渲染,然后执行下一个宏任务, 而微任务可以理解成在当前宏任务执行后立即执行的任务当一个
宏任务执行完,会在渲染前,将执行期间所产生的所有微任务都执行完宏任务 -> 微任务 -> GUI渲染 -> 宏任务 -> ...
常见微任务
- process.nextTick ()-Node
- Promise.then()
- catch
- finally
- Object.observe
- MutationObserver
4、练习
setTimeout(() => {
console.log(1);
}, 0);
Promise.resolve().then(() => {
console.log(2);
});
const test = () => {
return new Promise((resolve, reject) => {
console.log(3);
resolve(4);
console.log(5);
reject(6);
console.log(7);
});
};
test()
.then(val => {
console.log(8);
console.log(val);
})
.catch(err => {
console.log('err',err);
}).finally(() => {
console.log(9);
});
console.log(10);
考察知识点:
1、同步任务、异步任务
2、宏任务微任务
3、Promise构造函数是同步执行的,then方法是异步执行的
4、Promise resolve reject执行问题