深刻理解js引擎的执行机制

深刻理解js引擎的执行机制

最近在检讨,不少知识都是只会用,不理解底层的知识。因此在开发过程当中遇到一些奇怪的比较难解决的bug,在思考的时候就会收到限制。因此,在这里一点一点补充基础知识吧。前端

在阅读以前,请先记住两点:

  • js是单线程语言
  • js的Event Loop是js的执行机制。深刻理解js的执行,就等与深刻理解js的Event Loop

好啦,下面进入正题面试

1. 灵魂三问:js为何是单线程的?为何须要异步?单线程又是怎么实现异步的呢?

技术的出现,都跟现实世界里的场景密切相关。一样的,咱们就结合现实场景,来回答这三个问题。ajax

(1)js为何是单线程的?

        js是一种脚本语言,脚本语言是为了缩短传统的编写-编译-连接-运行过程而建立的计算机编程语言,脚本语言不须要编译,能够直接用,由解释器来负责解释。
        js最初被设计用在浏览器中,那么想象一下,若是浏览器中的js是多线程的。。。
场景描述:
        如今有两个进程:process1和process2,因为是多线程的js,因此它们对同一个DOM同时进行操做process1删除了该DOM,而process2编辑了该DOM,同时下达了两个矛盾的命令,你这让浏览器怎么执行呢?
        这样一想,是否是就理解了js为何被设计成单线程了吧~~~编程


(2)js为何是异步的?

场景描述:
        若是js不存在异步,只能自上而下执行,若是上一行执行时间很长,好比说没有网了,那么下面的代码就会被阻塞,对于用户来讲,阻塞就意味着“卡死”,这样致使用户体验不好。因为这个“缺陷”,致使JavaScript的全部网络操做,浏览器事件,都必须是异步执行。
        因此,js是存在异步执行的,好比setTimeout、setInterval、ajax、promisepromise


(3)单线程是怎么实现异步的?

场景描述:
        经过Event Loop(事件循环),因此说,理解了Event Loop机制,也就理解了js的执行机制啦。浏览器

2. js中的Event Loop第一课

举个栗子:观察下它的执行机制网络

console.log(1)
setTimeout(function(){
    console.log(2)
},0);
console.log(3)

毫无疑问:运行结果是1 3 2多线程

也就是说:setTimeout里的函数并无当即执行,而是延迟了一段时间,知足必定条件后才去执行的,咱们叫作异步代码。异步

因此这里咱们首先知道了js里的一种分类方式,就是将任务分为:同步任务和异步任务。编程语言

按照这种分类方式:js的执行机制是

  • 首先判断js是同步的仍是一步的,同步的就进入主进程,异步的就进入event table(事件表)
  • 异步任务在Event table中注册函数,当知足触发条件后,被推入event queue(事件队列)
  • 同步任务进入主线程后一直执行,直到主线程空闲时,才会去event queue中查看是否有可执行的异步任务,若是有就推入主进程中

以上三步循环执行,这就是Event Loop
因此上面的栗子,你是否能够描述它的执行顺序了呢?

console.log(1)          //是同步任务,放进主进程里
setTimeout(function(){  //是异步任务,放进event table,0s以后被推入event queue里
    console.log(2)
},0);
console.log(3)          //是同步任务,放进主进程里
//当一、3在浏览器控制台被打印后,主线程去event queue中查看是否有可执行的函数,执行setTimeout里的函数。

3. js中的Event Loop第二课

因此,上面关于Event Loop就是我对js执行机制的理解,是否是很简单呢。。。。
慢着!看看下面的这段代码
又一个栗子:

setTimeout(function(){
    console.log("定时器开始啦~~~");
})
new Promise(function(resolve){
    console.log("立刻执行for循环啦");
    for(var i=0;i<10000;i++){
        i==99&&resolve();
    }
}).then(function(){
    console.log("执行then函数啦")
});
console.log("表演完毕!");

有没有很熟悉,好像面试过程当中常常遇见这种问题呀~~~
如今,咱们按照上面学到的js执行机制去分析一下下

setTimeout(function(){                 //是异步任务,被放进event table中
    console.log("定时器开始啦~~~");
})
new Promise(function(resolve){         //是同步任务,被放进主进程中,直接执行
    console.log("立刻执行for循环啦");
    for(var i=0;i<10000;i++){
        i==99&&resolve();
    }
}).then(function(){                    //是异步任务,被放进event table中
    console.log("执行then函数啦")
});
console.log("表演完毕!");              //是同步任务,被放进主进程中,直接执行

运行结果是:【立刻执行for循环啦----表演完毕!----执行then函数啦----定时器开始啦~~~】

那么,难道是异步任务的执行顺序不是先后顺序,而是另有规定?事实上,按照同步和异步的划分方式,并不许确。

因此这里咱们首先知道了js里的一种分类方式,就是将任务分为:同步任务和异步任务。

叨叨了半天,原来都是一些“假大空”的东西,搞什么搞嘛,哼!!!

准确的划分方式是:

  • macro-task(宏任务):包括总体代码script、setTimeout、setInterval
  • micro-task(微任务):Promise、process.nextTick

图片描述

按照这种分类方式:js的执行机制就是

执行一个宏任务,执行过程当中若是遇到微任务,就将其放在微任务的event queue里
当前宏任务执行完成后,会查看微任务的event queue,并将里面的所有微任务依次执行完。
重复以上两个步骤,结合Event Loop第一课和Event Loop第二课,就是更为准确的js执行机制了。
尝试按照刚才学到的执行机制,去分析第二个栗子:

setTimeout(function(){              
    console.log("定时器开始啦~~~");
})
new Promise(function(resolve){     
    console.log("立刻执行for循环啦");
    for(var i=0;i<10000;i++){
        i==99&&resolve();
    }
}).then(function(){               
    console.log("执行then函数啦")    
});
console.log("表演完毕!");              
//首先执行script中的宏任务;
//遇到setTimeout,放进宏任务的event queue中;
//遇到new Promise直接执行,打印“立刻执行for循环啦”;
//遇到then方法,是微任务,被放进微任务的event queue
//执行打印“表演完毕!”,本轮宏任务执行完毕;
//查看本轮微任务,发现then方法里的函数,执行打印“执行then函数啦”
//到此,本轮Event Loop所有完成
//下一轮循环里,先执行一个宏任务,发现宏任务的event queue中有一个setTimeout中的函数,打印“定时器开始啦”

运行结果是:【立刻执行for循环啦----表演完毕!----执行then函数啦----定时器开始啦~~~】

4.谈谈setTimeout

下面这段setTimeout代码是什么意思?咱们通常说:“3s后,会执行setTimeout里的函数”

setTimeout(function(){
    console.log("执行了");
},3000)

可是这种说法并不严谨,准确的解释是:
        3s后,setTimeout里的函数会被推入到event queue中,而event queue(事件队列)里的任务,只有在主线程空闲时才会执行。
因此:只有在知足

  1. 3s后
  2. 主线程空闲

这两个条件同时知足,才会在3s后执行该函数。若是主线程执行内容不少,执行时间超过3s,好比执行了10s,那么这个函数只有在10s后执行啦


到此,js引擎的执行机制解读完啦,前端的水很深,慢慢摸着吧感谢文章的提供者:ziwei3749

相关文章
相关标签/搜索