很多人在面试的时候都会碰到这么一道面试题:给一段代码,写出执行结果和顺序。其中侧重的知识点可能也不尽相同。写这篇文章,主要是把其中可能涉及到的知识点都简单说一下,自己也好好梳理一下。如果文章有说的不对的地方,尽管diss。
浏览器内核分为渲染引擎和JS引擎,不过由于JS引擎越来越独立,内核就特指渲染引擎了。常见浏览器内核:IE-Trident引擎、Firefox-gecko引擎、Chrome/safari-webkit引擎、opera-Presto引擎后弃用使用webkit引擎、
- 渲染引擎:负责对网页语法的解释并渲染网页。渲染引擎决定了浏览器如何显示网页的内容以及页面的格式信息。不同的浏览器内核对网页编写语法的解释也有不同,因此同一网页在不同的内核的浏览器里的渲染(显示)效果也可能不同 。
- JS引擎:解释并编译代码,也就是执行JS代码的。单线程(如果是多线程,那对于同一个dom一个进程删除一个进程编辑,浏览器会很尴尬,不知道怎么办),那么问题来了,既然是单线程那如果遇到耗时很长的任务(加载超高清图片)那后面的任务岂不是会很生气,伟大的程序员当然有办法解决这个问题了,那就是把任务分为同步任务和异步任务,页面元素渲染这种就放到同步任务里面,数据请求,图片加载这种耗时长的就放进异步任务。盗图一张
有点抽象,来段代码解释一下
let data = [];
$.ajax({
url:www.javascript.com,
data:data,
success:() => {
console.log('发送成功!');
}
})
console.log('代码执行结束');
- ajax进入Event Table,注册回调函数success
- 执行console.log('代码执行结束')
- ajax事件完成,回调函数进入Event Queue
- 主线程从Event Queue读取回调函数并执行
上述过程不断重复,就是所谓的Event Loop(事件循环)
那如果用过promise的同学要问了,promise和setTimeout都是异步任务,执行顺序是怎样的呢。任务当然还有更精细的划分,不然提出这个问题解决不了,岂不是很尴尬。
- macro-task(宏任务):包括整体代码script,setTimeout,setInterval
- micro-task(微任务):Promise,process.nextTick
js代码执行的顺序(也是时间循环的顺序),相同类型任务会进入相同的Event Queue,进入整体代码(宏任务)后,开始第一次循环。接着执行所有的微任务。然后再次从宏任务开始,找到其中一个任务队列执行完毕,再执行所有的微任务。好吧,有点抽象,举个栗子
setTimeout(function() {
console.log('setTimeout');
})
new Promise(function(resolve) {
console.log('promise');
}).then(function() {
console.log('then');
})
console.log('console');
- 这段代码作为宏任务,进入主线程。
- 先遇到
setTimeout
,那么将其回调函数注册后分发到宏任务Event Queue。 - 接下来遇到了
Promise
,new Promise
立即执行,then
函数分发到微任务Event Queue。 - 遇到
console.log()
,立即执行。 - 这时整体代码script作为第一个宏任务执行结束,看看有哪些微任务?我们发现了
then
在微任务Event Queue里面,执行。 - ok,第一轮事件循环结束了,我们开始第二轮循环,当然要从宏任务Event Queue开始。我们发现了宏任务Event Queue中
setTimeout
对应的回调函数,立即执行。 - 结束。
这里再说一下setTimeout,都知道这是用来进行异步延迟任务的,但并不意味着代码是在设置的时间后执行,而是指经过设置的时间把要执行的任务加入到Event Queue中,又因为是单线程任务要一个一个执行,如果当前队列前面的任务需要的时间太久,那么只能等着,导致实际时间超过设置的时间。
setTimeout(fn,0)
的含义是,指定某个任务在主线程最早可得的空闲时间执行,意思就是不用再等多少秒了,只要主线程执行栈内的同步任务全部执行完成,栈为空就马上执行。要补充的是,即便主线程为空,0毫秒实际上也是达不到的。根据HTML的标准,最低是4毫秒。
对于setInterval(fn,ms)
来说,我们已经知道不是每过ms
秒会执行一次fn
,而是每过ms
秒,会有fn
进入Event Queue。一旦setInterval
的回调函数fn
执行时间超过了延迟时间ms
,那么就完全看不出来有时间间隔了。
JavaScript执行机制-Event Loop
最后来道题
//请写出输出内容
async function async1() {
console.log('async1 start');
await async2();
console.log('async1 end');
}
async function async2() {
console.log('async2');
}
console.log('script start');
setTimeout(function() {
console.log('setTimeout');
}, 0)
async1();
new Promise(function(resolve) {
console.log('promise1');
resolve();
}).then(function() {
console.log('promise2');
});
console.log('script end');
/*
输出结果:
script start
async1 start
async2
promise1
script end
async1 end
promise2
setTimeout
*/