彻底弄懂JavaScript 执行机制

很多人在面试的时候都会碰到这么一道面试题:给一段代码,写出执行结果和顺序。其中侧重的知识点可能也不尽相同。写这篇文章,主要是把其中可能涉及到的知识点都简单说一下,自己也好好梳理一下。如果文章有说的不对的地方,尽管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('代码执行结束');
  1.  ajax进入Event Table,注册回调函数success
  2. 执行console.log('代码执行结束')
  3. ajax事件完成,回调函数进入Event Queue
  4. 主线程从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。
  • 接下来遇到了Promisenew 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
*/

 

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值