让前端飞程序员

JavaScript工作原理之事件循环和基础的异步实现

2019-03-28  本文已影响24人  toyfish

事件循环


常规的JavaScript引擎是单线程的,也就是说所有的代码块都是顺序按照顺序被执行,这就导致遇到处理慢的代码块会阻塞软件的运行,甚至使程序停止响应,通常解决方案是使用异步来处理,把需要时间处理的代码块异步的进行处理,后面的块则继续执行,等到该代码块处理完以后再回过头来进行结果的处理,但是在ES6前并没有很好的异步解决方案,所以大部分是使用setTimeout来进行处理。下面就通过setTimeout的执行原理来理解一下JavaScript的异步处理的核心机制--事件循环机制。

首先看一下JavaScript的运行时模型:


下面基于这个模型,通过定时器来理解一下,JavaScript的事件循环机制以及异步是如何调用的。

首先写一个基本的定时器

[图片上传失败...(image-5c33a6-1553756162784)]

1.代码运行,此时进行代码的解析

2.调用console.log('HI') 进入到调用栈中

3.控制台打印Hi

4.解析下一部分代码

5.执行定时器,加入到调用栈中

6.在WebAPIs中创建一个Timer,并将定时器的内容移过去

7.定时器部分执行完毕,弹出调用栈,此时定时器内的内容被保存在WebAPIs环境当中

8.调用console.log('Bye') 进入到调用栈中

9.控制台打印Bye

10.console.log('Bye')弹出调用栈

11.等待WebAPIs中的timer执行,将cb1加入到回调队列中

12.通过事件循环将回调队列中的cb1重新压入到调用栈中

13.cb1内调用了console.log('cb1')所以也要压入到调用栈中

14.控制台打印cb1

15.弹出console.log('cb1')

16.弹出cb1

image.png

通过对setTimeout的流程解析,很容易发现JavaScript在运行时的调用过程是首先由JS引擎将代码解析编译,然后根据调用顺序加入到调用栈中(栈中的每一项都叫做帧)逐帧执行,其中需要用到WebAPIs、事件循环、回调队列的辅助,最后将执行的结果返回给调用处,至此JavaScript就完成了一次调用的循环。

基础异步实现


上面的例子已经使用setTimeout实现了一个基础的异步调用但是需要注意的是,虽然例子中使用的setTimeout(myCallback, 5000);但这并不意味着回调函数会在5秒后立即被执行,而是表示回调方法在5秒后把回调函数添加到回调队列中,如果此时队列中存在待处理任务,那么该回调函数也会相应的被延迟执行。

所以即使是像下面这个例子一样也依然会是一个异步的调用结果,因为setTimeout的第二个参数仅仅是延迟多久将回调内容放置到回调队列中,而不是确保延迟多久后一定执行。


console.log('Hi');

setTimeout(function() {

    console.log('callback');

}, 0);

console.log('Bye');

/**输出结果**/

//Hi

//Bye

//callback

上一篇下一篇

猜你喜欢

热点阅读