美文网首页让前端飞程序员
JavaScript工作原理之事件循环和基础的异步实现

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

作者: toyfish | 来源:发表于2019-03-28 14:56 被阅读24次

    事件循环


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

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

    • JavaScript引擎部分(例如V8引擎) ,黑框中部分

    • WebAPIs部分,由宿主环境提供的额外API不属于引擎的原生部分

    • EventLoop & CallbackQueue 事件循环和回调队列,同样属于宿主环境提供的机制,用于辅助引擎工作


    下面基于这个模型,通过定时器来理解一下,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
    
    

    相关文章

      网友评论

        本文标题:JavaScript工作原理之事件循环和基础的异步实现

        本文链接:https://www.haomeiwen.com/subject/lgbqbqtx.html