javascript基础修炼(5)—Event Loop(Node.js)

> 开发者的javascript造诣取决于对【动态】和【异步】这两个词的理解水平。 ![](upload/201809181159318120.png?x-oss-process=image/watermark,size_16,text_QDUxQ1RP5Y2a5a6i,color_FFFFFF,t_100,g_se,x_10,y_10,shadow_90,type_ZmFuZ3poZW5naGVpdGk=) ## 一. 一道考察异步知识的面试题 题目是这样的,要求写出下面代码的输出: ```js 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( )`的回调永远都无法被执行,尽管这看起来除了满足整蛊需求以外并没有什么明显的实用价值。 对Event Loop的理解,带给开发者的是**对代码整个生命周期更精细的控制能力**,尽管在依赖于SPA框架的开发中你几乎不会用到它们。 ## 二. Event Loop的原理 ![](upload/201809181159318272.png?x-oss-process=image/watermark,size_16,text_QDUxQ1RP5Y2a5a6i,color_FFFFFF,t_100,g_se,x_10,y_10,shadow_90,type_ZmFuZ3poZW5naGVpdGk=) *(上图来自下面推荐的这篇博文)* **【极力推荐文章】**: https://github.com/nswbmw/node-in-debugging/blob/master/3.6%20Event%20Loop.md 并不是笔者偷懒不想写这一节,而是在读过了这篇教程以后,自认为除非是剖析更底层的`libuv`的原理,否则仅就理解Event Loop而言,笔者自己认为不会比这篇写的清晰。 ## 三. 解析最后一题 上文中给出了从简单到复杂共6道题来供读者自检,算是非常贴心了,本文中针对最后一题进行一些讲解。**你会发现只要理解了Event Loop 的基本原理后,分析这类代码基本就是一个【完形填空】的过程**。 题目如下: ```js 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) ``` **题目分析:** 为了方便分析,先做代码分块: ![](http://i2.51cto.com/images/blog/201809/14/21815217b28008eb752cef40cefbedfb.png?x-oss-process=image/watermark,size_16,text_QDUxQ1RP5Y2a5a6i,color_FFFFFF,t_100,g_se,x_10,y_10,shadow_90,type_ZmFuZ3poZW5naGVpdGk=) 将代码块放入事件循环: ![](upload/201809181159336372.png?x-oss-process=image/watermark,size_16,text_QDUxQ1RP5Y2a5a6i,color_FFFFFF,t_100,g_se,x_10,y_10,shadow_90,type_ZmFuZ3poZW5naGVpdGk=) **分析:** 这里有必要说明一下`Fn2`的位置,文中并没有明确提及同步代码执行完毕后进入异步队列时会先经历`Tick`阶段,就图示而言,每一个宏观任务阶段之间都会检查`Tick`队列(你也可以理解为每次函数的调用栈被清空的时候会检查一次`Tick`队列),那么`Fn2`的待执行时序也就很好理解了。为了方便分析,将`console.log(n)`相关的方法称为`cln`。 接下来看一下当执行至`Fn2`时发生的事情: ![](upload/201809181159337663.png?x-oss-process=image/watermark,size_16,text_QDUxQ1RP5Y2a5a6i,color_FFFFFF,t_100,g_se,x_10,y_10,shadow_90,type_ZmFuZ3poZW5naGVpdGk=) **分析:** `Tick`队列中的`process.nextTick( )`回调会直接加入`Tick`队列(此处就可以实现篇头讲到的阻塞事件循环)。另外讲一下`CL6`这个回调,它上面绑定了一个100ms的定时器,在后续的`Timers`和`IO Polling`中都会检查倒计时是否到期,到了就执行,没到就等下一次`Timers`或`IO Polling`阶段再检查。从上例来看,推迟100ms的`CL6`在没有其他干扰的情况下几乎一定会在N个event loop以后才被执行。 同样的道理来拆分一下`Fn1`: ![](upload/201809181159337210.png?x-oss-process=image/watermark,size_16,text_QDUxQ1RP5Y2a5a6i,color_FFFFFF,t_100,g_se,x_10,y_10,shadow_90,type_ZmFuZ3poZW5naGVpdGk=) **分析:** `CL6`比`CL2`先开始计时,所以倒计时100ms先到,当然这是N个事件循环以后的事情了。 > 所以从上面的时序就可以看到输出的结果:**9 5 8 1 7 4 3 6 2** 【思考题】: 外加一个思考题,如果上例中`CL6`和`CL2`的延迟都是0,结果是怎样的呢? ## 四. requestAnimationFrame `requestAnimationFrame() `很多时候会被拿来和`setTimeout()`作对比,这个API是浏览器环境下为了实现高性能帧动画而设计的,它的主要目的是为了让浏览器的重绘能够配合显示设备的刷新率而去掉不必要的性能开销,常见的显示设备刷新率为`60Hz`,相当于你每1000/60≈16.7ms只能看屏幕一眼,得到的信息都是依靠这些离散画面的视觉暂留拼凑起来的,那如果动画元素在你看屏幕的时间间隔中像素位移过大的话,看起来就会是一卡一卡的,也就是平时常说的“丢帧”,从Event Loop的角度来讲的话,将其近似理解为`setTimeout(fn, 1000/刷新率)`就可以了。 编辑/寻水的鱼 本文首发于华为云社区:[原文链接](https://bbs.huaweicloud.com/blogs/04d6b508b7c611e89fc57ca23e93a89f)https://www.cnblogs.com/dashnowords/p/9649829.html
50000+
5万行代码练就真实本领
17年
创办于2008年老牌培训机构
1000+
合作企业
98%
就业率

联系我们

电话咨询

0532-85025005

扫码添加微信