事件驱动原理
简单地说 ,就是由于js代码只在一个线程上运行,他容易被阻塞。例如:复杂的算法运算,js进行复杂的dom操作等等。我们试想下如果一打开页面,就有大量IO请求,都是同步执行的话,页面会有多卡,所以事件驱动最终实际上是解决单线程通道阻塞问题,通过事件队列的方式给主通道让路
浏览器有两个线程,一个是js线程,一个是ui线程 ,两线程是互斥的,一旦一个线程阻塞,另一线程无法响应用户的操作
用户在界面上进行的操作称之为事件(Event)。由用户操作引发的一连串程序的动作,称之为事件驱动(Event-Driver).对事件进行处理程序或函数,我们称之为事件处理程序(Event Handler)
js虽然是单线程的,但是浏览器是多线程的,GUI渲染线程,js引擎线程,事件触发线程,定时器触发线程,异步请求http线程
当你打开chrome浏览器中的一个tab的时候,有多少线程在为你服务:
1.GUI渲染线程
2.js引擎线程
3.事件触发线程
4.定时器触发线程
5.异步请求http线程
宏任务和微任务
宏任务包括:setTimeout, setInterval, setImmediate, I/O, UI rendering
微任务: promise.then, process.nextTick, quequeMicrotask
为什么要设计宏任务和微任务,比如从客户端发起ajax请求向服务端获取数据,在此过程中,js引擎执行完了主线程上所有代码,在这时,他就要等待异步线程返回结果,为了提高执行效率,充分利用js引擎的资源,会让他去执行微任务,等所有的微任务执行完以后,再回到异步回调返回的结果里面执行相应的逻辑
微任务的性能要高于宏任务,因为微任务是v8引擎自带的api接口,而宏任务是v8引擎对底层定义宏任务api接口的封装
执行优先级:主线程上的任务> 微任务> 宏任务
事件轮询
js内存中有堆栈,堆中存对象,事件会入栈,如果栈中有异步任务,会将异步任务提交到异步模块进行处理,例如ajax请求,setTimeout,Dom等模块,提交完之后,会回到主线程上,继续执行他未完成的代码和任务,当完成代码和任务,会进入一个事件循环,在循环过程中,会定期去检查任务队列中有没有已完成的任务,任务队列中的任务大多都是由异步模块推送过来的已完成的结果
练习
看完这上面的,可以自己分析一下这些代码的执行顺序
console.log(1)
setTimeout(()=> {
console.log(2)
new Promise(() => {
console.log(11)
})
})
queueMicrotask(() => {
console.log(7)
})
let promise = new Promise((resolve, reject) => {
setTimeout(() => {
console.log(10)
})
resolve()
console.log(4)
})
fn()
console.log(3)
promise.then(() => {
console.log(12)
})
function fn() {
console.log(6)
}
答案是 1 4 6 3 7 12 2 11 10,你分析对了吗?