javascript运行机制:Event Loop

js是单线程的

  • 因为是单线程,所以所有任务都需要排队,前一个任务结束,后一个任务才能执行,如果前一个任务花费时间较长,后一个任务等待时间也随之变长。
  • js可以做到先把等待中的任务先放一边晾着,去处理后面的任务。于是所有任务可以分为两种,一种是同步任务,另一种是异步任务:
    • 同步任务很简单,前面的任务完成后面才能执行,一个接一个的执行任务。
    • 异步任务不占用主线程,直接进入“任务队列”中,等任务队列通知主线程,某个任务可以执行了,才会进入主线程执行。

异步执行的运行机制

1 所有同步任务都在主线程上执行,形成一个执行栈
2 主线程之外,还存在一个“任务队列”。只要异步任务有了执行结果,就在“任务队列”中放置一个事件
3 一旦执行栈中的所有同步任务都执行完毕,就会去“任务队列”中读取新的任务放到执行栈中,再依次执行任务
4 只要主线程空了,就会读取任务队列,这就是js的运行机制。这个过程会不断的重复


主线程和任务队列.jpg

再说说事件和回调函数

  • 任务队列其实存放的是事件的队列,主程序读取任务队列,其实就是在读有哪些事件罢了
  • 只要指定过回调函数,这些事件发生时就会进入任务队列中,等待主线程读取
  • 异步任务必须指定回调函数,当主线程执行异步任务时,其实就是在执行对应的回调函数
  • 任务队列是一个先进先出的数据结构,排在前面的先执行。当调用栈中的任务空了后,主线程会自动调用任务队列里的任务执行

来看看Event Loop

  • 主线程从任务队列中读取任务,这个过程是不断重复的,所以被称为Event Loop(事件循环),从字面意思就清楚了
  • 再来看一张图


    浏览器中的Event Loop.png

上图中,主线程产生了heap(堆)和stack(栈),栈中的代码调用各种api,然后在任务队列中加入click,load,done等事件,当栈中的任务都执行完后就去调用任务队列中的事件并依次执行

function one() {
    var a = 1;
    two();
    function two() {
        var b = 2;
        three();
        function three() {
            console.log(b);
        }
    }
}

one();
// 栈:是先进后出 函数调用就是最常见的形式
// one -> two -> three 依次执行
// 销毁过程是three -> two -> one

任务队列中的定时器

console.log(1);
setTimeout(function () {
    console.log('定时器');
},0);      // 如果不写时间,默认是4ms
let p = new Promise(function (resolve, reject) {
    console.log(3);
    resolve(100);
}).then(function (data) {
    console.log(data);
});
console.log(2);
// 1 3 2 100 '定时器' 

Node

先来看看node是如何工作的


node工作流程.png

NodeJs的运行机制:

1 V8引擎解析js代码
2 代码中可能会调用node API,node会交给LIBUV库处理
3 LIBUV通过阻塞I/O和多线程实现了异步I\O
4 将任务的执行结果返回给V8引擎,V8引擎再将结果返回给用户

Node中的Event Loop

在LIBUV内部有这样一个事件环机制,在node启动时会初始化事件环


node的Event Loop.png

这里每一个阶段都对应一个事件队列,当Event Loop执行到某一阶段的时候会将该阶段对应的事件依次执行。
当队列执行完毕or执行的数量超过上限的时候,会自动转入下一阶段。
这里我们重点关注一下poll阶段

poll阶段

poll阶段.png

总结一下:以上内容就是关于事件环的整体流程,也可以理解一下同步和异步的区别

你可能感兴趣的:(javascript运行机制:Event Loop)