浏览器中的Event Loop
我们都知道JavaScript是单线程的,也就是说同一时间只能干一件事。这是因为JavaScript主要是用来操作DOM的,如果变成多线程,浏览器就懵逼了,不知道该听谁的了。但是虽然js是单线程,但是完全可以模拟多线程,靠的就是Event Loop。
我们都知道js中的代码分 同步 和 异步,所谓的 异步 其实就是不会阻塞我们的主线程,等待主线程的代码执行完毕才会执行。callback setTimeout setInterval Promise … 这些都是都是我们耳熟能详的 异步 代码
js中的内存分为 堆内存(heap) 和 栈内存(stack), 堆内存 中存的是我们声明的object类型的数据,栈内存 中存的是 基本数据类型 以及 函数执行时的运行空间。我们的 同步 代码就放在 执行栈 中,那异步代码呢?浏览器会将 dom事件 ajax setTimeout等异步代码放到队列中,等待执行栈中的代码都执行完毕,才会执行队列中的代码。
1 | console.log(1); |
根据之前说的,setTimeout 会被放到队列中,等待执行栈中的代码执行完毕才会执行,所以会输出1, 3, 2
但是异步代码也是有区别的:这是因为 异步任务 分为 微任务(microtask) 和 宏任务(task),执行的顺序是 执行栈中的代码 => 微任务 => 宏任务。
执行栈
执行栈中的代码永远最先执行
微任务(microtask): promise、MutationObserver…
当执行栈中的代码执行完毕,会在执行宏任务队列之前先看看微任务队列中有没有任务,如果有会先将微任务队列中的任务清空才会去执行宏任务队列宏任务(task): setTimeout、setInterval、setImmediate(IE专用)、messageChannel…
等待执行栈和微任务队列都执行完毕才会执行,并且在执行完每一个宏任务之后,会去看看微任务队列有没有新添加的任务,如果有,会先将微任务队列中的任务清空,才会继续执行下一个宏任务
总体来说:
Event Loop 执行顺序如下所示:
- 首先执行同步代码,这属于宏任务
- 当执行完所有同步代码后,执行栈为空,查询是否有异步代码需要执行
- 执行所有微任务
- 当执行完所有微任务后,如有必要会渲染页面
- 然后开始下一轮 Event Loop,执行宏任务中的异步代码,也就是 setTimeout 中的回调函数
这里可能会有个误区,认为微任务快于宏任务,其实是错误的。因为宏任务中包括了 script ,浏览器会先执行一个宏任务,接下来有异步代码的话才会先执行微任务。