zoukankan      html  css  js  c++  java
  • 深入理解 JS 引擎执行机制(同步执行、异步执行以及同步中的异步执行)

    首先明确两点:

    1.JS 执行机制是单线程。

    2.JS的Event loop是JS的执行机制,深入了解Event loop,就等于深入了解JS引擎的执行。

    单线程执行带来什么问题?

    在JS执行中都是单线程执行,所以代码的执行可以说是自上而下,如果前一段的代码出现问题,就会导致下一段代码无法执行,对于用户而言就是卡死现象,所以在JS执行机制引出了异步执行操作。

    那异步能解决什么呢问题,又会带来什么问题?

    异步操作能够很好的解决上面单线程执行出现的卡死现象,但是也会产生问题,比如同时对一件事情操作,不知道应该先执行那件事。

    那么同步中使用异步如何实现呢?

    是通过的事件循环(Event loop)那先了解下Event loop吧。

    先看一下,以下代码(1)

    注意观察执行顺序

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


    有点JS 基础的同学会容易就知道了运行结果。

    运行结果是:1、3、2

    大家都知道setTimeout里的函数并没有立即执行,而是延迟一段时间,符合特定的条件才开始执行,这就是异步执行操作。

    由此执行我们先了解到JS任务的执行分类为:同步任务和异步任务。

    按照这种的分类方式JS的执行机制是:

    首先,判断JS是同步还是异步,同步进入主线程,异步进入Event table

    其次,异步任务在Event table中注册函数,当满足特定的条件,被推入Event queue

    最后,同步任务进入主线程后一直执行,直到主线程空闲后,才会去Event queue中查看是否有可执行的异步任务,如果有就推入主线程中执行。

    循环以上三步执行,这就是Event loop。

    所以上面的例子的执行顺序是

    console.log("1")是同步任务,放入主线程,
    setTimeout()是异步任务,被放入Event table,0秒后被推入Event queue里,
    console.log("3")是同步任务,放入主线程

    当1、3任务先执行完后,主线程去Event queue(事件队列)里查看是否有可执行的函数,执行setTimeout里的函数。



    所以,上面关于Event loop就是我对JS执行机制的理解,直到遇到了下面这段代码(2)。

    setTimeout(()=>{
    console.log("定时器开始执行");
    })
    
    new Promise(function(resolve){
        console.log("准备执行for循环了");
        for(var i=0;i<100;i++){
            i==22&&resolve();
        }
    }).then(()=>console.log("执行then函数"));
    
    console.log("代码执行完毕");

     那我们也按着上面总结的JS执行分类分析:

    setTimeout 是异步任务,被放到event table
    new Promise 是同步任务,被放到主进程里,直接执行打印 console.log('马上执行for循环啦')
    .then里的函数是 异步任务,被放到event table
     console.log('代码执行结束')是同步代码,被放到主进程里,直接执行

    所以最后的执行结果是:【准备执行for循环-->代码执行完毕-->定时器开始执行-->执行then函数 】,对吗?

    最后执行后发现结果并非是你想的这样,而是【准备执行for循环-->代码执行完毕-->执行then函数-->定时器开始执行】,为什么会是这样呢?难道是异步任务执行的顺序不是前后顺序,而是另有规定?事实上,按照异步同步的划分并不准确。

    而准确的方式应该是:

    • macro-task(宏任务):包括整体代码script,setTimeout,setInterval
    • micro-task(微任务):Promise,process.nextTic

     按照上图这种分类方式:JS 的执行机制是:

    • 执行一个宏任务,过程中如果遇到微任务,就将其放到微任务的【事件队列】里
    • 当前宏任务执行完成后,会查看微任务的【事件队列】,并将里面全部的微任务依次执行完

    重复以上2步骤,结合event loop(1) event loop(2) ,就是更为准确的JS执行机制了。

    按照这种执行机制,去分析第二段代码

    首先执行script下的宏任务,遇到setTimeout,将其放到宏任务的【队列】里
    遇到 new Promise直接执行,打印"准备执行for循环" 
    遇到then方法,是微任务,将其放到微任务的【队列里】
    打印
    "代码执行完毕"
    本轮宏任务执行完毕,查看本轮的微任务,发现有一个then方法里的函数, 打印
    "执行then函数"
    到此,本轮的event loop 全部完成。
    下一轮的循环里,先执行一个宏任务,发现宏任务的【队列】里有一个 setTimeout里的函数,执行打印
    "定时器开始执行"

    所以最后的执行顺序就是:【准备执行for循环-->代码执行完毕-->执行then函数-->定时器开始执行】

    最后再说一下setTimeout

    先看一段代码

    我们一般说这段代码,在3秒后执行setTimeout内的函数。

    setTimeout(function(){
        console.log('执行了')
     },3000)   

    但这种说法往往是不够严谨的,准确的解释是:3秒后,setTimeout函数会被推入Event queue(事件队列),而Event queue(事件队列)内的任务,只有在主线程空闲时才会执行。

    所以,只有同时满足以下条件setTimeout内的函数才能被执行

    1.3秒后

    2.主线程空闲时

    若主线程的执行任务很多,执行时间超过3秒,比如说5秒,那么setTimeout内的函数只能在5秒后执行了

    原文

     
     
     
  • 相关阅读:
    天才绅士少女助手克里斯蒂娜「推柿子」
    sum「莫队」
    simple,跳楼机,[同余系最短路]
    ceoi「chase」
    jzoj5195 数的划分
    lcis
    rectangle
    w
    v·y「状压dp」
    分手是住院「期望dp」
  • 原文地址:https://www.cnblogs.com/guoyeqiang/p/8317582.html
Copyright © 2011-2022 走看看