从简单的面试题入手了解js的运行机制

852 阅读4分钟

大家可能都遇到过类似的面试题

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

结果输出为 1   2  3

为什么结果不是1,3,2呢,我们明明只设置了0毫秒的延迟,下面让我们来看看到底是怎么回事


首先先用大白话简单介绍几个概念

我们都知道js是单线程的,什么是单线程,说白了就是js在同一时间只能做一件事,这就是单线程

那么什么是任务队列呢,我们都知道事件、ajax这些是异步操作,当我点击某个按钮时会通过回调函数执行某些操作,当我去请求接口的时候浏览器不用一直等着,它可以去做别的事情,当请求成功时会自动调起成功回调,那么这和js的单线程看起来是冲突的,那它又是怎么做到的呢,其实就是任务队列

说到任务队列这么还有概念,同步任务,异步任务, 像settimeout,ajax,promise这种就属于异步任务,像上述代码中的console.log(1)就属于同步任务

js代码是从上到下执行的,遇到异步任务要挂起,也就是说上面代码的settimeout会先挂起,先不执行,此时代码继续向下执行console.log(3),当同步任务都执行完成后才会去执行异步任务

所以刚才输出的结果是 1,2,3

下面我们来看下一道题

console.log("a")
while(1){
    
}
console.log("b")

只打印出a, 因为都是同步任务,while循环会一直执行,所有打印不出来b

那么我们再来看一道题

console.log("a");
setTimeout(()=>{
    console.log("b")
},0);
while(1){}

还是会只打印a,因为异步任务会当放到同步任务之后执行,所以b还是打印不出来

接下来再看一道题

for(var i=0;i<4;i++){
  setTimeout(()=>{
        console.log(i)
    },0)
}

最终会输出 4,4,4,4,for循环是一个同步任务,会先执行,此时会把setTimeout交给浏览器的time模块(主要是处理setTimeout,setInterval的),用不了1毫秒,循环执行完成,而此时r任务队列里还没有任务,只有setTimeout的时间(虽然我们设置的是0,但浏览器最小大概是4毫秒)到了,浏览器的Time模块才会把setTimeout的回调函数放到任务队列里,任务队列再等待一个叫event loop(后面会讲) 的东西来执行,这道题讲的是异步任务的放入时间和执行的时间


什么是event loop

                  


执行栈(stack)会执行同步任务,当遇到异步任务,浏览器的js引擎会将异步任务拿走,比如setTimeout,浏览器的time模块会将setTimeout拿走,当时间到了,会把回调函数放到任务队列(callback queue),当同步任务执行完成后,js引擎会去任务队列里看看有没有要执行的东西,发现里面有东西就会把它拿到执行栈中执行,此时setTimeout就变成了执行栈中的同步任务,当setTimeout执行完,执行栈又空了,js引擎会去任务队列中看看有没有其他需要执行的任务,如此往复的过程就是event loop


异步任务有哪些

setTimeout,setInterval

DOM事件

promise 

MutationObserve

MessageChannel等



看下面代码

console.log(1);
setTimeout(function(){
    console.log(2);
});
Promise.resolve(1).then(function(){
    console.log('promise')
})

会输出 1 , promise , 2

因为异步任务中又分为宏任务和微任务


微任务包括

promise里的then,

MutationObserve

MessageChannel

宏任务包括

setTimeout,setInterval

在浏览器端微任务的执行会优先于宏任务,而在node里会怎么样呢

再来看一段代码

console.log(1);
setTimeout(function(){
    console.log(2);
    Promise.resolve(1).then(function(){
        console.log('promise')
    })
})
setTimeout(function(){
    console.log(3);
})

在浏览器中会输出 1,2,promise,3

而用node运行则会输出 1,2,3,promise


在node中执行到相应的任务队列时,会把任务队列清空,里面的任务依次放到执行栈中执行,然后在去执行下一队列。


未完待续

如有错误,欢迎指正。图片均来自网络,侵删