开发者的javascript造诣取决于对【动态】和【异步】这两个词的理解水平。javascript
题目是这样的,要求写出下面代码的输出:java
setTimeout(() => { console.log(1) }, 0) new Promise((resolve, reject) => { console.log(2) for (let i = 0; i < 10000; i++) { i === 9999 && resolve() } console.log(3) }).then(() => { console.log(4) }) console.log(5)
若是没有详细钻研过异步队列,答对的可能性很低。题目的考察点很明确,就是javascript
中最核心的特色之一的【异步】,了解了原理之后,你就会明白javascript
中声称的“无阻塞”并非彻底成立的,经过一些小办法就可让setTimeout( )
的回调永远都没法被执行,尽管这看起来除了知足整蛊需求之外并无什么明显的实用价值。node
对Event Loop的理解,带给开发者的是对代码整个生命周期更精细的控制能力,尽管在依赖于SPA框架的开发中你几乎不会用到它们。git
(上图来自下面推荐的这篇博文)github
【极力推荐文章】:面试
https://github.com/nswbmw/node-in-debugging/blob/master/3.6%20Event%20Loop.md浏览器
并非笔者偷懒不想写这一节,而是在读过了这篇教程之后,自认为除非是剖析更底层的libuv
的原理,不然仅就理解Event Loop而言,笔者本身认为不会比这篇写的清晰。框架
上文中给出了从简单到复杂共6道题来供读者自检,算是很是贴心了,本文中针对最后一题进行一些讲解。你会发现只要理解了Event Loop 的基本原理后,分析这类代码基本就是一个【完形填空】的过程。异步
题目以下:函数
setImmediate(() => { console.log(1) setTimeout(() => { console.log(2) }, 100) setImmediate(() => { console.log(3) }) process.nextTick(() => { console.log(4) }) }) process.nextTick(() => { console.log(5) setTimeout(() => { console.log(6) }, 100) setImmediate(() => { console.log(7) }) process.nextTick(() => { console.log(8) }) }) console.log(9)
题目分析:
为了方便分析,先作代码分块:
将代码块放入事件循环:
分析:
这里有必要说明一下Fn2
的位置,文中并无明确说起同步代码执行完毕后进入异步队列时会先经历Tick
阶段,就图示而言,每个宏观任务阶段之间都会检查Tick
队列(你也能够理解为每次函数的调用栈被清空的时候会检查一次Tick
队列),那么Fn2
的待执行时序也就很好理解了。为了方便分析,将console.log(n)
相关的方法称为cln
。
接下来看一下当执行至Fn2
时发生的事情:
分析:
Tick
队列中的process.nextTick( )
回调会直接加入Tick
队列(此处就能够实现篇头讲到的阻塞事件循环)。另外讲一下CL6
这个回调,它上面绑定了一个100ms的定时器,在后续的Timers
和IO Polling
中都会检查倒计时是否到期,到了就执行,没到就等下一次Timers
或IO Polling
阶段再检查。从上例来看,推迟100ms的CL6
在没有其余干扰的状况下几乎必定会在N个event loop之后才被执行。
一样的道理来拆分一下Fn1
:
分析:
CL6
比CL2
先开始计时,因此倒计时100ms先到,固然这是N个事件循环之后的事情了。
因此从上面的时序就能够看到输出的结果:9 5 8 1 7 4 3 6 2
【思考题】:
外加一个思考题,若是上例中CL6
和CL2
的延迟都是0,结果是怎样的呢?
requestAnimationFrame()
不少时候会被拿来和setTimeout()
做对比,这个API是浏览器环境下为了实现高性能帧动画而设计的,它的主要目的是为了让浏览器的重绘可以配合显示设备的刷新率而去掉没必要要的性能开销,常见的显示设备刷新率为60Hz
,至关于你每1000/60≈16.7ms只能看屏幕一眼,获得的信息都是依靠这些离散画面的视觉暂留拼凑起来的,那若是动画元素在你看屏幕的时间间隔中像素位移过大的话,看起来就会是一卡一卡的,也就是平时常说的“丢帧”,从Event Loop的角度来说的话,将其近似理解为setTimeout(fn, 1000/刷新率)
就能够了。
编辑/寻水的鱼
本文首发于华为云社区:原文连接