• JS:事件循环机制、调用栈以及任务队列


    点击查看原文

    写在前面

    js里的事件循环机制十分有趣。从很多面试题也可以看出来,考察简单的setTimeout也就是考察这个机制的。
    在之前,我只是简单地认为由于函数执行很快,setTimeout执行时间即便为0也不会马上输出,而是等待函数执行完后再输出。这只对了一半。
    实际上其运行机制就是js中的事件循环机制,在这个循环机制中呢,又与call Stack和task queue有关。

    我的参考

    参考了两篇文章,所以文章内容与这两篇关系很大,加上了一些我自己的理解。

    深入浅出js事件循环机制(上) 
    深入浅出js事件循环机制(下)

    js事件循环机制

    事件循环机制呢,简单点来说,就是在执行上下文的过程中,对函数的入栈和出栈。执行前函数先入栈,执行完后函数出栈。如若遇到了一些异步操作像回调函数以及ajax、setTimeout等,会先将他们交给浏览器的其他模块去执行,执行完后,会把回调函数放入到taskqueue中。当所有的call stack执行完后再开始执行task queue中的函数。
    举一个简单的例子:

    1 console.log(1);
    2 setTimeout(function(){console.log(2);}, 0);
    3 console.log(3);

    我们来看一下执行的内部过程 
    1. 执行第一句,放入call stack中,输出 1 

     2. 第一句出栈,执行第二句,由于是异步执行,交给其他模块。 

     

    3. 执行完后,将回调函数放入taskqueue中 

     

     4. 执行下一句,同第一步一样,将语句入栈并执行,输出3 

    5. 语句出栈,此时call stack空了。开始执行task queue任务,输出2 

     

    所以,输出结果是 

     

     与预想一致。

    进阶

    如果添加了Promise又如何工作呢? 
    我们知道,Promise的回调函数不是传入的,而是使用then来调用的。因此,Promise中定义的函数应该是马上执行的,then才是其回调函数,放入queue队列中。 
    在参考的文章中还提到了一个重要的概念:

     macro-task包括:script(整体代码), setTimeout, setInterval, setImmediate, I/O, UI rendering。 

    micro-task包括:process.nextTick, Promises, Object.observe, MutationObserver
    执行顺序:函数调用栈清空只剩全局执行上下文,然后开始执行所有的micro-task。当所有可执行的micro-task执行完毕之后。循环再次执行macro-task中的一个任务队列,执行完之后再执行所有的micro-task,就这样一直循环。

     再看一个例子:

    (function test() {
        setTimeout(function() {console.log(4)}, 0);
        new Promise(function executor(resolve) {
            console.log(1);
            for( var i=0 ; i<10000 ; i++ ) {
                i == 9999 && resolve();
            }
            console.log(2);
        }).then(function() {
            console.log(5);
        });
        console.log(3);
    })()

    具体的过程可以看上面那篇文章。大概过程如下:
    1. 遇到setTimeout,交给其他模块执行,执行完后回调放入macro-task中
    2. 遇到Promise,立即执行里面的function,输出1。
    3. 循环开始,遇到resolve(),修改Promise状态为fulfill。继续执行,输出2。
    4. 遇到then,将回调放入micro-task中。
    5. 继续执行,输出3。
    6. call stack执行完毕了。开始执行micro-task中的回调函数,输出5。
    7. micro-task执行完毕,开始执行macro-task中的回调函数,输出4。
    8. 结束。

  • 相关阅读:
    吴恩达深度学习第4课第3周编程作业 + PIL + Python3 + Anaconda环境 + Ubuntu + 导入PIL报错的解决
    Ubuntu 14.04 16.04 17.10 + Win10 双系统安装记录 + 分区大小选择办法
    NVIDIA Titan Xp Star Wars Collector's Edition显卡深度学习工作站 + Ubuntu17.10 + Tensorflow-gpu + Anaconda3 + Python 3.6 设置
    request.getParameter() request.getAttribute()
    Gson 数据解析
    级联查询
    mybatis 从入门到精通 读书笔记
    springboot 随笔
    springboot 跨域
    select
  • 原文地址:https://www.cnblogs.com/taohuaya/p/10432475.html
Copyright © 2020-2023  润新知