您的位置:首页 > 常用工具 > 这一次,彻底弄懂 JavaScript 执行机制
这一次,彻底弄懂 JavaScript 执行机制

这一次,彻底弄懂 JavaScript 执行机制

类别:常用工具    更新时间:2023-04-29 11:01:24

  • 16

    人气值

  • 19

    已收藏

  • 16

    正在玩

无广告安全小编亲测

简介推荐16相关
  • 这一次,彻底弄懂 JavaScript 执行机制

无论你是新手还是老手,无论你是在面试工作还是在做日常的开发工作,我们经常会遇到这样的情况:给定几行代码,我们需要知道它的输出内容和顺序。 因为它是一种单线程语言,所以我们可以得出结论:

按照语句出现的顺序执行

看到这里,读者要打人了:难道我不知道js是一行一行执行的吗? 你需要说什么? 不要不耐烦,因为js是一行一行执行的,所以我们认为js是这样的:

let a = '1';console.log(a);
let b = '2';console.log(b);

这一次,彻底弄懂 JavaScript 执行机制

但实际上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('代码执行结束');

这一次,彻底弄懂 JavaScript 执行机制

按照js是按照语句出现的先后顺序执行的理念,我自信的记下输出结果:

//"定时器开始啦"//"马上执行for循环啦"//"执行then函数啦"//"代码执行结束"

上去验证,结果完全错了,瞬间懵了。 是否逐行执行约定?

这一次,彻底弄懂 JavaScript 执行机制

我们真的要把执行机制搞清楚。

关于

它是一种单线程语言。 Web-是在最新的HTML5中提出的,但单线程核心并没有改变。

所以所有版本的“多线程”都是单线程模拟的,所有多线程都是纸老虎!

事件循环

由于js是单线程的,就好像银行只有一个窗口。 客户需要排队办理业务。 同样,js任务也要一个一个执行。

如果一个任务花费的时间太长,那么后一个任务也必须等待。 那么问题来了,如果我们想浏览新闻,但是新闻中包含的超高清图片加载的很慢js页面加载完成后执行,我们的网页是不是应该一直卡到图片全部显示出来?

所以聪明的程序员将任务分为两类:

当我们打开网站时,网页的渲染过程是很多同步的工作,比如页面骨架和页面元素的渲染。 而加载图片、音乐等占用资源多、耗时长的任务,则属于异步任务。

这部分有严格的文字定义,但本文的目的是以最小的学习成本彻底理解执行机制,所以我们用一张图来说明:

这一次,彻底弄懂 JavaScript 执行机制

如果地图中要表达的内容用文字表达:

我们不禁要问,我们怎么知道主线程执行栈是空的呢? js引擎有一个进程,它会不断的检查主线程执行栈是否为空。 一旦为空,就会去Event Queue中查看是否有等待调用的函数。

说了这么多文字,直接写一段代码更直接:

let data = [];$.ajax({    url:www.javascript.com,    data:data,    success:() => {        console.log('发送成功!');    }})console.log('代码执行结束');

上面是一段简单的ajax请求代码:

相信通过上面的文字和代码,你对js的执行顺序有了初步的了解。 接下来,让我们研究高级主题:。

爱与恨

著名的就不用多说了。 大家对他的第一印象就是异步执行可以延时。 我们经常通过这种方式来实现延迟3秒:

setTimeout(() => {    console.log('延时3秒');},3000)

渐渐地,用到的地方越来越多,问题也随之而来。 有的时候明明写的是延时3秒,但是函数实际是5、6秒后才执行。 怎么了?

我们先来看一个例子:

setTimeout(() => {    task();},3000)console.log('执行console');

根据我们之前的结论,是异步的,应该先执行.log的同步任务,所以我们的结论是:

//执行console//task()

验证一下,结果是正确的!

然后我们修改之前的代码:

setTimeout(() => {    task()},3000)
sleep(10000000)

乍一看差不多,但是当我们执行这段代码的时候,我们发现控制台执行task()需要3秒多的时间。 约定的延迟为 3 秒。 为什么现在需要这么长时间? ?

这时候我们需要重新理解定义。 先来说说上面代码是如何执行的:

上面的流程完成后,我们知道这个函数是在指定的时间后,将待执行的任务(本例中的task())添加到Event Queue中,由于是单线程的待执行任务其一,如果之前的任务耗时过长,则只能等待,导致真正的延迟时间远远超过3秒。

我们也经常遇到类似(fn,0)这样的代码,0秒后执行是什么意思? 可以立即执行吗?

答案是不。 (fn,0)的含义是指定一个任务在主线程最早可用的空闲时间执行,也就是说不需要等待很多秒,只要主线程执行完所有的同步堆栈中的任务。 完成,栈为空时立即执行。 例如:

//代码1console.log('先执行这里');setTimeout(() => {    console.log('执行啦')},0);

//代码2console.log('先执行这里');setTimeout(() => {    console.log('执行啦')},3000);

代码1的输出是:

//先执行这里//执行啦

代码2的输出是:

//先执行这里// ... 3s later// 执行啦

我要补充的是,即使主线程是空的,0毫秒其实也是不可达的。 根据 HTML 标准,最小值为 4 毫秒。 感兴趣的同学可以自行学习。

恨爱

说完以上,当然不能少了它的孪生兄弟。 它们很相似,只是后者是循环执行。 对于执行顺序,注册的函数会按指定的时间间隔放入Event Queue,如果之前的任务耗时太长,也需要等待。

唯一需要注意的是,对于(fn, ms),我们已经知道fn不会每隔ms秒执行一次,而是每隔ms秒就会进入Event Queue。 一旦回调函数fn的执行时间超过延迟时间ms,那么就完全没有时间间隔了。 请仔细阅读这句话。

和。()

传统的定时器我们已经研究过了,接下来我们探讨一下用.()的性能。

.()的定义和作用类似于node.js版本中的"",在事件循环的下一个循环中调用回调函数。

让我们进入正题。 除了广义的同步任务和异步任务,我们还有更细化的任务定义:

不同类型的任务会进入对应的Event Queue,例如,会进入同一个Event Queue。

事件循环的顺序决定了js代码的执行顺序。 进入整体代码(宏任务)后js页面加载完成后执行,开始第一个循环。 然后执行所有微任务。 然后再从宏任务开始,找到其中一个要执行的任务队列,然后执行所有的微任务。

听起来有点绕,我们用文章开头的一段代码来说明一下:

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

事件循环、宏任务和微任务的关系如图所示:

这一次,彻底弄懂 JavaScript 执行机制

下面来分析一段比较复杂的代码,看看你是否真的掌握了js的执行机制:

console.log('1');
setTimeout(function() { console.log('2'); process.nextTick(function() { console.log('3'); }) new Promise(function(resolve) { console.log('4'); resolve(); }).then(function() { console.log('5') })})process.nextTick(function() { console.log('6');})new Promise(function(resolve) { console.log('7'); resolve();}).then(function() { console.log('8')})
setTimeout(function() { console.log('9'); process.nextTick(function() { console.log('10'); }) new Promise(function(resolve) { console.log('11'); resolve(); }).then(function() { console.log('12') })})

第一轮事件循环流程分析如下:

好了,第一轮事件循环正式结束,这一轮的结果是输出1,7,6,8。那么第二轮时间循环从宏任务开始:

整个代码,一共三个事件循环,完整的输出是1,7,6,8,2,4,3,5,9,11,10,12。

请注意node环境下的事件监听依赖libuv和前端环境不完全一样,可能输出顺序不对。

写在最后

(1)异步js

一开始我们就说它是单线程语言。 不管新框架新语法糖实现了什么样的所谓异步,其实都是通过同步的方式模拟出来的。 牢牢掌握单线程语言非常重要。

(2) 事件循环Event Loop

事件循环是js实现异步的一种方法,也是js的执行机制。

(3) 执行及运作

执行和操作之间有很大的区别。 在不同的环境下,比如node、、Ringo等,执行方式不同。 操作多是指分析引擎,是统一的。

(4)

微任务和宏任务的种类很多,比如等等,执行上有共同点,有兴趣的同学可以自行学习。

(5) 最后最后

牢牢抓住两个基本点,专心认真学习,早日实现成为前端高手的伟大梦想!

这一次,彻底弄懂 JavaScript 执行机制

- 结尾 -

这一次,彻底弄懂 JavaScript 执行机制

》《面试官都在用的题库,快来看看》

展开内容
精彩推荐 +
相关推荐 +
游戏资讯 +