JavaScrip的执行机制

JavaScrip的执行机制

J a v a S c r i p t 是 一 门 单 线 程 语 言 , E v e n t L o o p 是 J a v a S c r i p t 的 执 行 机 制 JavaScript是一门单线程语言,Event Loop 是JavaScript的执行机制 JavaScript线EventLoopJavaScript

前言

我们都知道,JavaScript是一门 单线程语言 ,所以不少人得出这样的结论,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('代码执行结束');

骚年,自信的亮出你的剑吧!你以为的代码,是这样的:

定时器开始啦 -> 马上执行for循环啦 -> 执行then函数啦 -> 代码执行结果

但真的是这样吗? 去chrome上验证下,发现不对,于是就懵逼了,觉得自己只是一个前端打字员。

# Chrome上的直接结果:
马上执行for循环啦 -> 代码执行结束 -> 执行then函数啦 -> 定时器开始啦

明确核心

首先,我们需要明确一点,那就是:JavaScript是一门单线程语言,如果你听到JavaScript版的“多线程”都是单线程模拟出来的。哪怕是最新HTML5中提出的 Web-Worker1 ,都遵循单线程这一核心思想。

事件循环

既然js是单线程的,那如果我们需要打开淘宝,那是不是要等淘宝的图片全部呈现出来才可以滚动使用呢?如果这样,那岂不是要等待好久才能偶使用软件吗?为此,聪明的程序员将浏览器执行的任务分为两大类,

  • 同步任务
  • 异步任务

当我们再次打开淘宝时,页面首先渲染同步任务,如页面的骨架、元素等,而像图片、视频这些耗时久的任务就变成异步任务,在同步任务执行完后执行。

下图可以是浏览器的Evnet Loop运行流程,是一道经典的面试题哦!!!

在这里插入图片描述

我的愚见是这样的:

  1. 任务进入执行栈时,首先判断这段代码是同步任务还是异步任务
  2. 如果是同步任务,则进入到浏览器的主线程中运行,一直执行完
  3. 如果是异步任务,那么将进入 Event Table 中注册,注册完成后进入到 Event Queue 中等待
  4. JS引擎会持续不断的检查主线程执行站是否为空,即主线程的同步任务是否全部完成
  5. 一旦主线程的所有同步任务完成,就会去 Event Queue 内读取对应的异步任务,将其调入到主线程中执行

我们人可以通过肉眼计算得知同步任务全部执行完了,那么计算机又是怎么知道的呢?经过研究发现,原来 JS引擎存在 monitoring process 进程中,会持续不断的检查主线程执行栈是否为空,一旦为空,就会去调用 Event Queue 内的代码执行到主线程中。

说了这么多,有没有把你绕晕呢?还是直接上案例来更好的理解吧!

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

上面是一段简易的 ajax 请求代码,我们可知,

  • 在ajax进入 Evnet Table 时注册函数success进入到 Event Queue 中,
  • 然后执行主线程的同步任务,“代码执行结束”。
  • 当主线程的任务执行完成时进入到Event Queue 中调用注册的success函数任务。

异步任务

setTimeout

对于 setTimeout ,我们的第一反应是:异步任务+延时执行。

没错,不过我们需要注意的是:setTimeout(fn,0) 。这里的0指的并不是在JS代码中的0秒,而是指在主线程的同步任务完成后立即执行该函数。而且还有一点,0毫秒在实际上是达不到的哦,最快的也是4毫秒。

setInterval

我们既然介绍了 setTimeout ,当然不能只偏爱一个,也要介绍它的孪生兄弟:setInterval 。setInterval 会在每隔一段指定的时间内将注册的函数置入到 Event Queue 中执行。

这里需要注意的是,setInterval(fn,ms) 中ms秒会执行一次一次fn。但是如果 setInterval 的回调函数 fn 执行时间超过了延迟时间 ms,那么就完全看不出来我们设置的时间间隔了。

Promise与process.nextTick(callback)

new Promise 实例化的过程中所执行的代码都是同步执行的,而 .then.catch 中的回调都是异步执行的。

所有的异步都是指事件回调中的那部分代码

执行顺序:同 > 微任务 > 宏任务

同步任务:
  • console.log
  • new Promise 实例化的过程
异步任务:
  • 宏任务:

    浏览器环境下NODE环境下
    I/OYY
    setTimeoutYY
    setIntervalYY
    setImmediateNY
    requestAnimationFrameYN
    UI事件Y

    注意:

    ​ 在 Promise/A+ 的规范中,Promise既可以是微任务也可以是宏任务,但是普遍将它当作微任务阵营的

    requestAnimationFrame 在MDN中定义为:下次页面重回前所执行的操作,而重绘也是作为宏任务的一个步骤来存在的,且该步骤晚于微任务的执行

  • 微任务:

    浏览器环境下NODE环境下
    process.nextTickNY
    MutationObserverYN
    Promise中的 .then.catch.finallyYN
  • 好处:

    • 不会阻塞主线程上的执行,不会卡死浏览器
    • 只需要设置好异步程序,就可以去干别的事情了
async/await函数

async/await函数本质上是基于 Promise 的一些封装,而 Promise 是属于为任务的一种.

所以,

  • async 函数在关键字 await之前的代码都相当于 new Promise 时传入的同步代码;
  • await 之后的所有代码都相当于 Promise.then 中的异步代码
注意
  1. 一个Event Loop 可以有一个或多个事件队列,但是只有一个微任务队列
  2. 微任务队列全部执行完全会重新渲染一次
  3. 每个宏任务执行完都会重新渲染一次
  4. requestAnimationFrame 处于渲染阶段,不在微任务队列中

面试题:

  1. 浏览器 Event Loop 的执行流程:

    答案已在文章内

  2. Event Loop这个流程中的坑 ---- 定时器不准.
    ​ 主线程中的全部同步任务执行时间如果大于定时器设置的时间,会导致定时器的回调函数在延后执行

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值