浅谈,JavaScript 运行机制和Event Loop

155 阅读3分钟
JavaScript是一个非常灵活的语言且是单线程

所谓是单线程就好比你去一个银行,银行只开设了一个窗口,然而一个窗口就好比一个线程,取钱的人就好比JS代码。也就是说

  • javascript是按照语句出现的顺序执行的

其实这样说是很严谨,例如:

setTimeout(

function(){ console.log('定时器开始啦') }); 

new Promise(

function(resolve){ console.log('马上执行for循环啦'); 

 for(var i = 0; i < 100; i++)

{ i == 99 && resolve(); } 

}).then(

function(){ console.log('执行then函数啦') 

}); 

 console.log('代码执行结束');

你觉得会是一行一行的执行吗?为什么会出现我们不一样的结果呢,一行一行的执行对我们执行效果很不好那么怎么解决呢?这就涉及到JS 中的

  • 同步任务
  • 异步任务

何为同步呢?简单的理解就是你向一个女孩子表白,你打电话给她,说喜欢她。这个过程你一直不挂电话,啥事也不干就等她回答,对于你来说就是同步。比如页面骨架和页面元素的渲染就是同步,而异步呢,我进常用AJAX请求。下面一张图让我们来体会一下:


那么这张图说明了什么呢?我们看看

  • 同步和异步任务分别进入不同的执行"场所",同步的进入主线程,异步的进入Event Table并注册函数。
  • 当指定的事情完成时,Event Table会将这个函数移入Event Queue。
  • 主线程内的任务执行完毕为空,会去Event Queue读取对应的函数,进入主线程执行。
  • 上述过程会不断重复,也就是常说的Event Loop(事件循环)。
  • 我们不禁要问了,那怎么知道主线程执行栈为空啊?js引擎存在monitoring process进程,会持续不断的检查主线程执行栈是否为空,一旦为空,就会去Event Queue那里检查是否有等待被调用的函数。

    那我们来看一下代码吧,加深理解。

    var  data={}

    $.ajax(

     url:xxxxxx,

     data:data, 

     success:() => {

     console.log('发送成功!'); 

     }

     }) 

    console.log('代码执行结束');

    有人这么平淡无奇的AJAX有啥奇怪,不妨我来看看这段代码的含义,
    • ajax进入Event Table,注册回调函数success
    • 执行console.log('代码执行结束')
    • ajax事件完成,回调函数success进入Event Queue。
    • 主线程从Event Queue读取回调函数success并执行。

    有的同学就觉得我明白了,下面我再来一段代码看看,

    console.log('main1');
    
    process.nextTick(function() {
        console.log('process.nextTick1');
    });
    
    setTimeout(function() {
        console.log('setTimeout');
        process.nextTick(function() {
            console.log('process.nextTick2');
        });
    }, 0);
    
    new Promise(function(resolve, reject) {
        console.log('promise');
        resolve();
    }).then(function() {
        console.log('promise then');
    });
    
    console.log('main2');

    不妨我们来分析下:

    1. 开始执行全局SCRIPT宏任务,输出 main1,process.nextTick 放入tickTaskQueen,setTimeout放入 macroTaskQueen, new Promise 执行 输出 promise,then 方法 放入 MicroTaskQueen , 接着 最后一行代码 console.log 输出 main2
    2. 当前的 宏任务执行完毕,开始清空微任务,先清空tickTaskQueen ,执行 console.log('process.nextTick1'); 输出'process.nextTick1;再清空MicroTaskQueen执行 console.log('promise then'); 输出promise then;微任务全部清空。
    3. 开始下次 eventLoop; 执行 setTimeout; 第一行 console.log('setTimeout'); 输出setTimeout; process.nextTick 将任务放入了tickTaskQueen;当前宏任务执行完毕;开始清空MicroTaskQueen,清空tickTaskQueen ,执行 console.log('process.nextTick2');输出process.nextTick2;

    顺便附上图:


    这幅图很好说明时间循环的机制吧,好了这次就到这里,欢迎大家提意见。