美文网首页
JavaScript 运行机制及Event Loop

JavaScript 运行机制及Event Loop

作者: nucky_lee | 来源:发表于2019-12-10 15:25 被阅读0次

    1.堆通常是一个可以被看做一棵树的数组对象。堆总是满足下列性质:

    a. 堆中某个节点的值总是不大于或不小于其父节点的值;
    b. 堆总是一棵完全二叉树。将根节点最大的堆叫做最大堆或大根堆,根节点最小的堆叫做最小堆或小根堆。常见的堆有二叉堆、斐波那契堆等。

    2.堆是在程序运行时申请某个大小的内存空间,即动态分配内存;而栈只是指一种使用堆的方法。

    1.栈(stack)又名堆栈,是一个数据集合,也可以理解为只能在一端进行插入或删除操作的列表。这一端被称为栈顶,相对地,把另一端称为栈底。
    2.栈就是一个桶,遵循先进后出原则;
    3.栈(Stack)是操作系统在建立某个进程时或者线程(在支持多线程的操作系统中是线程)为这个线程建立的存储区域,该区域具有FIFO的特性,在编译的时候可以指定需要的Stack的大小。

    队列

    是一种支持先进先出(FIFO)的集合,即先被插入的数据,先被取出!

    JavaScript运行机制:

    作为浏览器脚本语言,JavaScript的主要用途是与用户互动,以及操作DOM。这决定了它只能是单线程,否则会带来很复杂的同步问题。

    为了利用多核CPU的计算能力,HTML5允许JavaScript脚本创建多个线程,但是子线程完全受主线程控制,且不得操作DOM。所以,这个新标准并没有改变JavaScript单线程的本质。

    执行栈

    当javascript代码执行的时候会将不同的变量存于内存中的不同位置:堆(heap)和栈(stack)中来加以区分。其中,堆里存放着一些对象。而栈中则存放着一些基础类型变量以及对象的指针。 但是我们这里说的执行栈和上面这个栈的意义却有些不同。

    js 在执行可执行的脚本时,首先会创建一个全局可执行上下文globalContext,每当执行到一个函数调用时都会创建一个可执行上下文(execution context)EC。当然可执行程序可能会存在很多函数调用,那么就会创建很多EC,所以 JavaScript 引擎创建了执行上下文栈(Execution context stack,ECS)来管理执行上下文。当函数调用完成,js会退出这个执行环境并把这个执行环境销毁,回到上一个方法的执行环境... 这个过程反复进行,直到执行栈中的代码全部执行完毕。

    下面来看个简单的例子:

    function fun3() {
    
        console.log('fun3')
    
    }
    
    function fun2() {
    
        fun3();
    
    }
    
    function fun1() {
    
        fun2();
    
    }
    
    fun1();
    

    当执行一个函数的时候,就会创建一个执行上下文,并且压入执行上下文栈,当函数执行完毕的时候,就会将函数的执行上下文从栈中弹出。知道了这样的工作原理,让我们来看看如何处理上面这段代码:

    1.执行全局代码,创建全局执行上下文,全局上下文被压入执行上下文栈

    ECStack = [
    
        globalContext
    
    ];
    

    2.全局上下文初始化

    globalContext = {
    
        VO: [global],
    
        Scope: [globalContext.VO],
    
        this: globalContext.VO
    }
    

    3.初始化的同时,fun1 函数被创建,保存作用域链到函数的内部属性[[scope]]

    fun1.[[scope]] = [
    
        globalContext.VO
    
    ];
    

    4.执行 fun1 函数,创建 fun1 函数执行上下文,fun1 函数执行上下文被压入执行上下文栈

    ECStack = [
    
        fun1,
    
        globalContext
    
    ];
    

    5.fun1函数执行上下文初始化:

    1. 复制函数 [[scope]] 属性创建作用域链,
    2. 用 arguments 创建活动对象,
    3. 初始化活动对象,即加入形参、函数声明、变量声明,
    4. 将活动对象压入fun1 作用域链顶端。
      同时 f 函数被创建,保存作用域链到 f 函数的内部属性[[scope]]
    checkscopeContext = {
        AO: {
    
                arguments: {
     
                length: 0
    
            },
    
            scope: undefined,
    
            f: reference to function f(){}
        },
    
        Scope: [AO, globalContext.VO],
    
        this: undefined
    }
    

    6.执行 fun2() 函数,重复步骤2。

    7.最终形成这样的执行栈:

    ECStack = [
    
        fun3
    
        fun2,
    
        fun1,
    
        globalContext
    
    ];
    

    8.fun3执行完毕,从执行栈中弹出...一直到fun1

    事件队列

    主线程执行栈执行时遇到异步任务,开辟分线程执行异步任务并在异步任务执行完后放入任务队列(注意,此时只是异步事件执行完成,其中的回调函数并没有去执行)。异步任务队列分为微任务队列和宏任务队列,同一次事件循环中,微任务永远在宏任务之前执行。主线程任务执行完会立刻先处理所有微任务队列中的事件,然后再去宏任务队列中取出最早放入的事件放入执行栈中,执行其中的回调同步代码。如此反复,这样就形成了一个无限的循环。这就是这个过程被称为“事件循环(Event Loop)”。

    队列采取先进先出的原则;栈先进后出;

    为了更好地理解Event Loop,请看下图

    bg2014100802.png

    主线程运行的时候,产生堆(heap)和栈(stack),栈中的代码调用各种外部API,它们在"任务队列"中加入各种事件(click,load,done)。只要栈中的代码执行完毕,主线程就会去读取"任务队列",依次执行那些事件所对应的回调函数。

    macro task与micro task

    在介绍之前,我们先看一段经典的代码执行:

    setTimeout(function () {
    
        console.log(1);
    
    });
    
    new Promise(function(resolve,reject){
    
        console.log(2)
    
        resolve(3)
    
    }).then(function(val){
    
        console.log(val);
    
    })
    

    会看到控制台先后分别输出:2、3、1。

    先看一下阮老师对setTimeout的一些解释:

    setTimeout(fn,0)的含义是,指定某个任务在主线程最早可得的空闲时间执行,也就是说,尽可能早得执行。它在"任务队列"的尾部添加一个事件,因此要等到同步任务和"任务队列"现有的事件都处理完,才会得到执行。需要注意的是,setTimeout()只是将事件插入了"任务队列",必须等到当前代码(执行栈)执行完,主线程才会去执行它指定的回调函数。要是当前代码耗时很长,有可能要等很久,所以并没有办法保证回调函数一定会在setTimeout()指定的时间执行。

    实际上,一般因为异步任务之间并不相同,因此他们的执行优先级也有区别。不同的异步任务被分为两类:微任务(micro task)和宏任务(macro task)。

    以下事件属于宏任务:

    • setTimeout
    • MessageChannel
    • postMessage
    • setImmediate

    以下事件属于微任务

    • new Promise()
    • new MutaionObserver()

    参考:

    JavaScript 运行机制详解:再谈Event Loop

    http://www.ruanyifeng.com/blog/2014/10/event-loop.html

    Event loop 机制简介

    https://github.com/muwoo/blogs/issues/14

    相关文章

      网友评论

          本文标题:JavaScript 运行机制及Event Loop

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