EventLoop事件循环

一、先了解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事件循环)