美文网首页
EventLoop事件循环

EventLoop事件循环

作者: stillpeng | 来源:发表于2019-10-08 14:34 被阅读0次

    一、先了解javascript为什么是单线程

        |--javascript语言的特点:单线程。
        |--线程和进程
            |--进程:运行的程序就是一个进程,比如正在运行的浏览器就是一个进程。
            |--线程:程序中独立运行的代码段,一个进程由单个或多个线程组成,线程是负责执行代码的。
        |--JS为什么单线程?多线程效率多高啊?
            |--1.首先决定单线程的主要原因是js的用途:用户交互和操作DOM
            |--2.举个例子:两个线程,一个线程在DOM节点添加内容,另一个线程删除了这个节点。
            |--3.上述例子浏览器应该以哪个线程为准?
            |--综上:产生了问题。
        |--为了避免复杂性,单线程成为了javascript的核心特征。
        |--但是为了利用多核CPU的计算能力,HTML5提出了Web Worker标准,允许js创建多个线程,但是子线程    
            完全受主线程控制,且不得操作DOM,新标准并没有改变javascript单线程本质。

    二、同步执行和异步执行

        |--同步执行:因为JS语言特点是单线程,即任务是串行的,后一个任务要等前一个任务执行完,才能执行,这样在主线程按照顺序,串行执行的任务称为同步执行任务。
        |--异步执行:由于类似于Ajax网络请求、setTimeout时间延迟、DOM事件的用户监护等,这些任务并不消耗CPU,是一种空等,资源浪费,因此出现了异步。通过将任务交给异步处理模块去处理,主线程的效率能大大的得到提升,可以并行的处理其他操作。当异步处理完成,主线程空闲时间,主线程读取相应的callback,进行后续操作,最大程度的利用CPU。因此异步执行就是CPU跳过等待,先处理后续任务(CPU和网络模块、timer模块等并行进行任务)。
        |--而为了协调主线程和异步模块之间性的工作,就产生了任务队列和事件循环

    三、Event Loop事件循环机制

    事件循环机制

        |--逐步分析事件循环机制:
            |--1.主线程运行js代码,产生了堆和执行栈。
                |--堆:对象被分配在堆中,即用来表示大部分非机构化的内存区域。
                |--执行栈:execution context stack,运行同步代码。执行栈中的代码(同步任务),总是在读取
                task queue(异步任务)之前。
            |--2.主线程遇到异步任务,指给对应的异步处理模块(异步进程)进行处理(web API)
            |--3.异步进程处理完毕(Ajax返回、DOM事件处理、Timer到时等),将相应的异步任务推入任务队列。
                |--任务队列:任务队列是一个事件队列,IO设备完成一项任务,就在task queue里添加一个事件,表示
                相关的异步任务可以进入“执行栈”了。主线程读取任务队列,就是读取里面的那些事件(数据结构是
                先进先出)。主线程的读取过程是自动的,只要执行栈一清空,任务队列中的第一个事件就会自动
                进入主线程。
                |--回调函数:任务队列也被称为callback queue(回调队列),即异步任务必须指定回调函数,当主线程
                开始执行异步任务时,就是执行对应的回到函数。
                |--异步进程包括:
                    |--类似于onclick,由浏览器内核的DOM binding模块处理,事件触发时,回调函数添加到任务队列。
                    |--setTimeout,由浏览器内核的Timer模块处理,事件到达,回调函数添加到任务队列。
                    |--Ajax,由浏览器内核的Network模块处理,网络请求返回后,添加到任务队列。
            |--4.主线程循环的去查找、执行任务队列中的事件,就形成是事件循环(event loop)。

    四、宏任务和微任务

        |--将任务进行更精细的定义,分为宏任务和微任务。
            |--宏任务队列(macrotask queue) 
                |--不唯一,存在一定的优先级(用户I/O部分优先级更高),异步执行,同一个事件循环中只执行一个,
                包括:整体代码script,setTimeout、setInterval、ajax、dom操作。
            |--微任务队列(microtask queue)
                |--整个事件循环当中仅存在一个,执行为同步,同一个事件循环中的microtask会按照队列顺序,
                串行执行。微任务指的是ES6中的Promise。
        |--宏任务和微任务区别:
            |--微任务中所有的callback处在同一个事件循环中,宏任务中的callback有自己的事件循环。
            |--利用微任务可以形成一个同步执行环境,但是如果微任务太长,将导致宏任务等待太久,长时间
            执行不了,最终导致用户的I/O无响应,所以慎用使用。
        |--加入宏任务和微任务概念的js运行机制:
            |--1.“执行栈”最先执行所有的同步代码(宏任务)。执行完毕。
            |--2.检查是否有微任务(microtask),如果有执行所有微任务。
            |--3.取出“任务队列”中的事件对应的回调函数(宏任务)进入执行栈。执行完毕。
            |--4.再检查是否有微任务,有的话执行多有微任务。
            |--5.主线程不断重复执行3,4形成事件循环。
        |--示例:

    宏任务、微任务示例

        |--运行机制分析:
            |--同步环境:1 -> 2 -> 3
            |--事件循环(微任务):5
            |--事件循环(宏任务):4

    五、宏任务之setTimeout和setInterval

        |--两个都是定时器,内部运行机制完全相同,区别在意setTimeout一次性执行,而setInterval反复执行。
        |--setTimeout和setInterval产生的任务都是异步任务,且是宏任务。
        |--两个定时器都是接受两个参数
            |--fn:callback 函数
            |--time:推迟执行的毫秒数、反复执行的毫秒数。
        |--注意:如果第二个参数为0,并不是立即执行,而是指定某个任务在主线程最早可得的空闲时间执行,
        也就是“尽早执行”。它在任务队列的“尾部”添加一个事件,因此要等同步任务和“任务队列”现有的事件处理完
        才能得到执行。
        |--setTimeout(function(){},3000)是异步任务,先被放入event table,3秒后才被推入到task queue,
        而task queue任务队列里的任务,只有主线程空闲时,才会执行。这样一来如果同步任务超出了
        推迟执行的事件3000s,那么这个3000秒就没有什么意义了,setTimeout(function(){},3000)就等同于
        setTimeout(function(){},0)。

    六、微任务之Promise

        |--当new Promise(function(){... ...}).then(microTask),Promise里的function会立即执行,但是then方法里的
        函数是在执行栈后,任务队列之前执行的,即微任务。

    七、nodeJs中的process.nextTick

        |--node.js中提供的和“任务队列”有关的方法,他产生的任务是放在执行栈的尾部,并不属于宏任务或
        微任务。因此它的任务总是发生在异步任务的前面。

    八、nodeJs中的setImmediate

        |--他产生的任务在“任务队列”的尾部。

    九、总结

        |--任务执行的优先级:
            |--1.同步代码(宏任务)
            |--2.process.nextTick(node.js中的方法)
            |--3.Promise(微任务)
            |--4.setTimeout(fn)、setInterval(fn)等(宏任务)
            |--5.setImmediate(nodejs)
            |--6.setTimeout(fn,time>0)、setInterval (fn,time>0)
        |--示例1:同步任务>异步任务 [优先级]

    同步>异步

        |--示例2:证明任务队列中,先进先出。

    先进先出

        |--示例3:在fn()之前增加一个微任务,Promise里的内容同步执行,先输出8,9后才是6。

    Promise里的内容同步执行

        |--示例4:Promise形成了同步执行环境。但是then里的内容会在同步后,任务队列前执行。

    then同步后,task queue前

    十、扩展

        |--案例:for循环+setTimeout问题    

        |--如何让其输出0~4?
        |--解决办法1:var该为let

    let解决

        |--解决办法2:通过闭包

    闭包解决

        |--解决办法3:添加立即执行的函数

    函数解决

    相关文章

      网友评论

          本文标题:EventLoop事件循环

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