js 作为一个单线程的脚本语言, 并不具备什么并行啊之类的能力, 但是我们用 js 会用来处理各种各样的任务, 比如说我们需要发起一个网络请求, 但是这时候我们的用户还需要做别的交互, 我们不可能说 你先给我等着吧, 等我请求完你再接着操作, 这个对于用户来讲是一件非常操蛋的事情, 那怎么来解决这种很操蛋的事情呢, 答案来了 就是异步了, 也就是 event loop 机制.
event loop
event loop 可以理解为实现异步的方式, 我们的浏览器执行 js 时, 会存在堆栈, 还有一个 queue
引自 mdn
我们可以用个例子来描述下这个
例子
我们可以简单画个图来说明下
image.png
image.png
image.png
image.png
通过例子, 我们可以看出会有一个 queue, 是一个队列的结构用来存储异步执行的函数, 我们将存储在 queue 里面的 js 操作 称为 task, 我在 w3c 规范 https://www.w3.org/TR/2017/REC-html52-20171214/webappapis.html#event-loops-processing-model 中看了下 event loop 的运行机制.
大概概括下:
先将找到 task queue 中的最早进入的 task, 如果有的话就执行, 没有则进行
microtask checkpoint
microtask 其实就是检查 microtask queue, 并按照先进先出的顺序, 依次执行 microtask. // 这里需要注意如果在执行过程中, 有 microtask 的产生的话, 新的 microtask 会直接进入到 microtask queue 的尾部
然后执行浏览器渲染
回到第一步
那哪些任务是 microtask, 哪些是 task?
task 任务源:
- setTimeout
- setInterval
- setImmediate
- I/O
- UI rendering
microtask 没有明确的规范, 通常将以下这些认为是 microtask 源:
- process.nextTick
- promises
- Object.observe
- MutationObserver
看了上面这些, 我们再来写个例子 验证下
image.png
上面的发生主要过程:
检查当前 task queue 是否为空. 要注意的是 这时候 setTimeout 还是没有发生的, 其实就是没有的
跳到 setTimeout 这一行, 发现 setTimeout 并将其推入 task queue
跳转到 mircrotask 开始执行, 也就是开始执行 Promise.then 这一段代码, 依次打印 2
3
执行完成后又回到第一步检查 task queue 是否为空, 这时候发现了有 setTimeout, 开始执行, 打印 1
好了, 以上就是我对 event loop 的认识. 写下来做个总结经验
参考
- w3c event-loops https://www.w3.org/TR/2017/REC-html52-20171214/webappapis.html#event-loops
- Tasks, microtasks, queues and schedules https://jakearchibald.com/2015/tasks-microtasks-queues-and-schedules/
从 event loop 规范探究 javaScript 异步及浏览器更新渲染时机 https://github.com/aooy/blog/issues/5
来源: http://www.jianshu.com/p/7425ce20f395