zoukankan      html  css  js  c++  java
  • JS执行机制详解

    JS执行机制详解

    一、总结

    一句话总结:

    JS是单线程语言,Event Loop(事件循环)是JS的执行机制。

    1、为什么JS是单线程?

    1、【操作dom】:JS作为浏览器脚本语言,它的主要用途是与用户互动,以及操作DOM,因此js是单线程,也避免了同时操作同一个DOM的矛盾问题;比如,假定JavaScript同时有两个线程,一个线程在某个DOM节点上添加内容,另一个线程删除了这个节点,这时浏览器应该以哪个线程为准?
    2、【避免复杂】:为了避免复杂性,从一诞生,JavaScript就是单线程,这已经成了这门语言的核心特征,将来也不会改变。
    3、【“多线程”实际上指的是“多子线程”,完全受控于主线程】:为了利用多核CPU的计算能力,H5的Web Worker实现的“多线程”实际上指的是“多子线程”,完全受控于主线程,且不允许操作DOM;

    2、js为什么需要异步?

    JS是单线程语言,如果JS中不存在异步,只能自上而下执行,如果上一行解析时间很长,那么下面的代码就会被阻塞。对于用户而言,阻塞就意味着"卡死",这样就导致了很差的用户体验

    3、js单线程是如何实现异步的?

    是通过事件循环(event loop),JavaScript的任务分为两种,在主线程上执行的任务"同步任务",被主线程挂载起来的任务"异步任务",后者一般是放在一个叫任务队列的数据结构中。

    4、异步任务-回调函数-任务队列?

    1、一般我们绑定一个事件,比如点击事件等等,都是在某一个时刻才触发执行的,这个时候就得放到任务队列里面,等待执行,而在某个DOM节点上绑定了事件,就要有相应的回调函数,它们是相辅相成的。
    2、所谓回调函数,就是那些被挂载起来,等待执行的代码,主线程执行任务队列里面的异步任务,其实就是执行这些回调函数。

    5、宏任务和微任务(异步任务) 介绍?

    不同类型的异步任务会进入不同的Event Queue,有宏任务的队列和微任务的队列。

    6、常见的 宏任务和微任务(异步任务)?

    宏任务(macro-task):整体代码script、setTimeOut、setInterval
    微任务(mincro-task):promise.then、process.nextTick(node)

    7、下列异步过程分析?

    |||-begin

    console.log(1)
    
    setTimeout(function(){
      console.log(2)
    },0)
    setTimeout(function(){
      console.log(3)
    },1000)
    
    console.log(4)
    
    执行结果:
    // 1 4 2 3

    |||-end

    a、console.log(1)是同步任务,直接打印1;
    b、setTimeout是异步任务,且是宏函数,放到宏函数队列中,等待下次Event Loop才会执行;
    c、console.log(4)是同步任务,直接打印4;
    d、主线程执行完毕,没有微任务,那么执行第二个宏任务setTimeout,打印2;

    二、JS执行机制详解

    转自:JS执行机制详解_JavaScript_一个女胖子的博客-CSDN博客
    https://blog.csdn.net/ZD717822023/article/details/97491152

    • JS是单线程语言
    • Event Loop是JS的执行机制。深入了解JS的执行,就等于深入了解JS里的event loop(JS执行机制是一样的,但运行机制可能不一样 nodejs中)

    解释

    1. 为什么JS是单线程?
    2. 为什么需要异步?
    3. 单线程是如何实现异步的?

    一、为什么JS是单线程

    1. JS作为浏览器脚本语言,它的主要用途是与用户互动,以及操作DOM,因此js是单线程,也避免了同时操作同一个DOM的矛盾问题;比如,假定JavaScript同时有两个线程,一个线程在某个DOM节点上添加内容,另一个线程删除了这个节点,这时浏览器应该以哪个线程为准?
    2. 为了避免复杂性,从一诞生,JavaScript就是单线程,这已经成了这门语言的核心特征,将来也不会改变
    3. 为了利用多核CPU的计算能力,H5的Web Worker实现的“多线程”实际上指的是“多子线程”,完全受控于主线程,且不允许操作DOM;

    二、为什么需要异步

    1. 单线程就意味着,所有任务需要排队,前一个任务结束,才会执行后一个任务。如果前一个任务耗时很长,后一个任务就不得不一直等着。
    2. 如果JS中不存在异步,只能自上而下执行,如果上一行解析时间很长,那么下面的代码就会被阻塞。对于用户而言,阻塞就意味着"卡死",这样就导致了很差的用户体验

    三、单线程是如何实现异步的

    是通过事件循环(event loop),理解了event loop机制,就理解了JS的执行机制.
    JavaScript语言的设计者意识到,所以他将JavaScript的任务分为两种,在主线程上执行的任务"同步任务",被主线程挂载起来的任务"异步任务",后者一般是放在一个叫任务队列的数据结构中。

    1. 任务队列

    任务队列就是一个事件队列,其中最重要的是异步任务事件和定时事件

    • 异步任务事件:一般我们绑定一个事件,比如点击事件等等,都是在某一个时刻才触发执行的,这个时候就得放到任务队列里面,等待执行,而在某个DOM节点上绑定了事件,就要有相应的回调函数,它们是相辅相成的。
      所谓回调函数,就是那些被挂载起来,等待执行的代码,主线程执行任务队列里面的异步任务,其实就是执行这些回调函数。
    • 定时事件:setInterval 和 setTimeout

    2. 同步任务和异步任务

    • 同步任务(synchronous):在主线程上排队执行的任务,只有前一个任务执行完毕,才能执行后一个任务;
    • 异步任务(asynchronous):不进入主线程、而进入"任务队列"(task queue)的任务,只有"任务队列"通知主线程,某个异步任务可以执行了,该任务才会进入主线程执行。 (异步任务事件,定时事件,页面渲染请求图片获取异步资源等)
      异步执行机制如下:
      • 所有同步任务都在主线程上执行,形成一个执行栈。
      • 主线程之外,还有一个“任务队列”,只要异步任务有了运行结果,就在“任务队列”之中放置一个事件。
      • 一旦“执行栈”中的所有同步任务执行完毕了,系统就会读取“任务队列”,看看里面有哪些事件。那些对应的异步任务,于是结束等待状态,进入执行栈,开始执行。
      • 主线程不断重复上面的三步。(事件循环)

    3. 宏任务和微任务

    不同类型的任务会进入不同的Event Queue,有宏任务的队列和微任务的队列。
    这里需要注意的是new Promise是会进入到主线程中立刻执行,而promise.then则属于微任务。
    事件循环的顺序,决定js代码的执行顺序。进入整体代码(宏任务)后——开始第一轮循环执行完所有微任务——下一轮宏任务…

    • 宏任务(macro-task):整体代码script、setTimeOut、setInterval
    • 微任务(mincro-task):promise.then、process.nextTick(node)

    4. Event Loop

    综合以上事件循环步骤如下:

    1. 整体的script(作为第一个宏任务)开始执行的时候,会把所有代码分为两部分:“同步任务”、“异步任务”;
    2. 同步任务会直接进入主线程依次执行;
    3. 异步任务会再分为宏任务和微任务;
    4. 宏任务进入到Event Table中,并在里面注册回调函数,每当指定的事件完成时,Event Table会将这个函数移到Event Queue中;
    5. 微任务也会进入到另一个Event Table中,并在里面注册回调函数,每当指定的事件完成时,Event Table会将这个函数移到Event Queue中;
    6. 当主线程内的任务执行完毕,主线程为空时,会检查微任务的Event Queue,如果有任务,就全部执行,如果没有就执行下一个宏任务;
    7. 上述过程会不断重复,这就是Event Loop事件循环;

    5. 事例代码

    一、例子1
    console.log(1)
    
    setTimeout(function(){
      console.log(2)
    },0)
    setTimeout(function(){
      console.log(3)
    },1000)
    
    console.log(4)
    
    执行结果:
    // 1 4 2 3
    

    分析:

    1. console.log(1)是同步任务,直接打印1;
    2. setTimeout是异步任务,且是宏函数,放到宏函数队列中,等待下次Event Loop才会执行;
    3. console.log(4)是同步任务,直接打印4;
    4. 主线程执行完毕,没有微任务,那么执行第二个宏任务setTimeout,打印2;
    5. 结果:1,4,2,3
    二、例子2
    setTimeout(function(){
        console.log(1)
    });
    
    new Promise(function(resolve){
        console.log(2);
        for(var i = 0; i < 10000; i++){
            i == 9999 && resolve();
        }
    }).then(function(){
        console.log(3)
    });
    
    console.log(4);
    
    执行结果:
    // 2, 4, 3, 1
    

    分析:

    1. setTimeout是异步,且是宏函数,放到宏函数队列中;
    2. new Promise是同步任务,直接执行,打印2,并执行for循环;
    3. promise.then是微任务,放到微任务队列中;
    4. console.log(4)同步任务,直接执行,打印4;
    5. 此时主线程任务执行完毕,检查微任务队列中,有promise.then,执行微任务,打印3;
    6. 微任务执行完毕,第一次循环结束;从宏任务队列中取出第一个宏任务到主线程执行,打印1;
    7. 结果:2,4,3,1
    三、例子3
    console.log(1);
    
    setTimeout(function() {
      console.log(2);
    }, 0);
    
    Promise.resolve().then(function() {
      console.log(3);
    }).then(function() {
      console.log('4.我是新增的微任务');
    });
    
    console.log(5);
    
    执行结果:
    // 1,5,3,4.我是新增的微任务,2
    

    分析:

    1. console.log(1)是同步任务,直接执行,打印1;
    2. setTimeout是异步,且是宏函数,放到宏函数队列中;
    3. Promise.resolve().then是微任务,放到微任务队列中;
    4. console.log(5)是同步任务,直接执行,打印5;
    5. 此时主线程任务执行完毕,检查微任务队列中,有Promise.resolve().then,执行微任务,打印3;
    6. 此时发现第二个.then任务,属于微任务,添加到微任务队列,并执行,打印4.我是新增的微任务;
    7. 这里强调一下,微任务执行过程中,发现新的微任务,会把这个新的微任务添加到队列中,微任务队列依次执行完毕后,才会执行下一个循环;
    8. 微任务执行完毕,第一次循环结束;取出宏任务队列中的第一个宏任务setTimeout到主线程执行,打印2;
    9. 果:1,5,3,4.我是新增的微任务,2
    四、例子4
    function add(x, y) {
      console.log(1)
      setTimeout(function() { // timer1
        console.log(2)
      }, 1000)
    }
    add();
    
    setTimeout(function() { // timer2
      console.log(3)
    })
    
    new Promise(function(resolve) {
      console.log(4)
      setTimeout(function() { // timer3
        console.log(5)
      }, 100)
      for(var i = 0; i < 100; i++) {
        i == 99 && resolve()
      }
    }).then(function() {
      setTimeout(function() { // timer4
        console.log(6) 
      }, 0)
      console.log(7)
    })
    
    console.log(8)
    
    执行结果
    //1,4,8,7,3,6,5,2
    

    分析:

    1. add()是同步任务,直接执行,打印1;
    2. add()里面的setTimeout是异步任务且宏函数,记做timer1放到宏函数队列;
    3. add()下面的setTimeout是异步任务且宏函数,记做timer2放到宏函数队列;
    4. new Promise是同步任务,直接执行,打印4;
    5. Promise里面的setTimeout是异步任务且宏函数,记做timer3放到宏函数队列;
    6. Promise里面的for循环,同步任务,执行代码;
    7. Promise.then是微任务,放到微任务队列;
    8. console.log(8)是同步任务,直接执行,打印8;
    9. 此时主线程任务执行完毕,检查微任务队列中,有Promise.then,执行微任务,发现有setTimeout是异步任务且宏函数,记做timer4放到宏函数队列;
    10. 微任务队列中的console.log(7)是同步任务,直接执行,打印7;
    11. 微任务执行完毕,第一次循环结束;
    12. 检查宏任务Event Table,里面有timer1、timer2、timer3、timer4,四个定时器宏任务,按照定时器延迟时间得到可以执行的顺序,即Event Queue:timer2、timer4、timer3、timer1,取出排在第一个的timer2;
    13. 取出timer2执行,console.log(3)同步任务,直接执行,打印3;
    14. 没有微任务,第二次Event Loop结束;
    15. 取出timer4执行,console.log(6)同步任务,直接执行,打印6;
    16. 没有微任务,第三次Event Loop结束;
    17. 取出timer3执行,console.log(5)同步任务,直接执行,打印5;
    18. 没有微任务,第四次Event Loop结束;
    19. 取出timer1执行,console.log(2)同步任务,直接执行,打印2;
    20. 没有微任务,也没有宏任务,第五次Event Loop结束;
    21. 结果:1,4,8,7,3,6,5,2
    五、例子5
    setTimeout(function() { // timer1
      console.log(1);
      setTimeout(function() {  // timer3
        console.log(2);
      })
    }, 0);
    setTimeout(function() {  // timer2
      console.log(3);
    }, 0);
    
    执行结果
    //1,3,2
    

    分析:

    1. 第一个setTimeout是异步任务且宏函数,记做timer1放到宏函数队列;
    2. 第三个setTimeout是异步任务且宏函数,记做timer2放到宏函数队列;
    3. 没有微任务,第一次Event Loop结束;
    4. 取出timer1,console.log(1)同步任务,直接执行,打印1;
    5. timer1里面的setTimeout是异步任务且宏函数,记做timer3放到宏函数队列;
    6. 没有微任务,第二次Event Loop结束;
    7. 取出timer2,console.log(3)同步任务,直接执行,打印3;
    8. 没有微任务,第三次Event Loop结束;
    9. 取出timer3,console.log(2)同步任务,直接执行,打印2;
    10. 没有微任务,也没有宏任务,第四次Event Loop结束;
    11. 结果:1,3,2

    参考文章

    1. JavaScript 运行机制详解:再谈Event Loop
      http://www.ruanyifeng.com/blog/2014/10/event-loop.html
    2. 深入理解JS引擎的执行机制
      https://segmentfault.com/a/1190000012806637
    3. js运行机制详解(Event Loop)
      https://www.jianshu.com/p/e06e86ef2595
    4. 这一次,彻底弄懂 JavaScript 执行机制
      https://juejin.im/post/59e85eebf265da430d571f89#heading-9
     
     
     
  • 相关阅读:
    Encrypted Handshake Message
    RSAParameters Struct
    What if JWT is stolen?
    What's the difference between JWTs and Bearer Token?
    RSA Algorithm Example
    第18届Jolt大奖结果公布
    Ruby on rails开发从头来(windows)(三十六) 调试技巧
    Ruby on rails开发从头来(四十二) ActiveRecord基础(主键和ID)
    YouTube开放基础技术架构 让用户建自家YouTube
    Ruby on rails开发从头来(四十) ActiveRecord基础(Boolean属性)
  • 原文地址:https://www.cnblogs.com/Renyi-Fan/p/12521837.html
Copyright © 2011-2022 走看看