• JavaScript EventLoop


    javascript 其本身是一门  单线程的  非阻塞的  脚本语言,单线程意味着,javascript代码在执行的任何时候,都只有一个主线程来处理所有的任务,非阻塞靠的就是 event loop(事件循环)

    但是浏览器确实多线程的,所以才会可以打开多个标签页,从而打开不同的页面。

    Event Loop它最主要是分三部分:主线程、宏任务队列(macrotask)、微任务队列(microtask)

    js的任务队列分为同步任务异步任务,所有的同步任务都是在主线程里执行的,异步任务可能会在macrotask或者microtask里

    在事件循环中,每进行一次循环操作称为tick,tick 的任务处理模型是比较复杂的:分别是 Macrotask (宏任务)和 Microtask(微任务)。

    主线程

    其实就是进行 同步任务

    宏任务队列(macrotask)包含:

    setTimeout、setInterval、setImmediate、I/O、UI rendering

    微任务队列(microtask)包含:

    promise.then, MutaionObserver, process.nextTick(Node.js 环境)

    执行顺序

    1、先执行主线程,即同步任务

    2、遇到宏任务队列(macrotask)放到宏任务队列(macrotask)

    3、遇到微任务队列(microtask)放到微任务队列(microtask)

    4、主线程执行完毕

    5、执行微任务队列(microtask),微队任务列(micro task)执行完毕

    6、执行一次宏任务队列(macro task)中的一个任务,执行完毕

    7、执行微队列(micro task),执行完毕

    8、依次循环。。。

    规范:先同步,后异步, 先执行微观任务,再执行宏观任务

    console.log('开始');
    
    setTimeout(function () {
    
      console.log('setTimeout1');
    
    });
    
    new Promise(resolve => {
    
      console.log('Promise');
    
      resolve();
    
      setTimeout(() => console.log('setTimeout2'));
    
    }).then(function () {
      
      console.log('Promise Then')
    
    })
    
    console.log('结束');

    我们按照步骤来分析下:

    1、遇到同步代码,先打印 “开始” 。
    2、遇到setTimeout异步,放入队列,等待执行 。
    3、中途遇到Promise函数,函数直接执行,打印 “Promise”。
    4、遇到setTimeout ,属于异步,放入队列,等待执行。
    5、遇到Promise的then等待成功返回,异步,放入队列。
    6、遇到同步,打印 “结束”。
    7、执行完,从头开始,将异步队列中的代码,按顺序执行。有一个微观任务,then后的,所以打印 “Promise Then”,再执行两个宏观任务 “setTimeout1” “setTimeout2”。

    所以,打印的顺序为:开始、 Promise、 结束 、 Promise Then、 setTimeout1、 setTimeout2.

    console.log(1)
    process.nextTick(() => {
      console.log(2)
      setTimeout(() => {
        console.log(3)
      })
    })
    setTimeout(() => {
      console.log(4)
      new Promise(() => {
        console.log(5)
      })
    })
    requestIdleCallback(() => {
      console.log(6)
    })

    // 特殊说明: new Promise()属于主线程任务 let promise = new Promise((resolve,reject) => { setTimeout(() => { console.log(7) }) resolve() // 这个console也属于主线程任务 console.log(8) }) fn() console.log(9) promise.then(() => { console.log(10) }) function fn(){ console.log(11) }

    结果是1、8、11、9、10、2、4、5、7、3、6、

    这个写法可以囊括80%以上的event loop循环机制的场景了,下面开始梳理具体的运行机制。

    js是从上到下执行的,所以:

    1. 遇到同步代码,先打印的是 1
    2. 遇见了process.nextTick,因为它属于微队列 (microtask) 放入微任务队列里,等待执行 
    3. 遇到setTimeout,setTimeout是属于宏队列(macrotask)异步,放入宏任务队列,等待执行 
    4. 遇到promise在实例化的时候,
      1. setTimeout是属于宏队列(macrotask)异步,放入宏任务队列,等待执行 ,
      2. 并执行了成功的方法,在有promise.then的调用的时候就会去出发,但这里不做打印,
      3. 接着遇到了console,这里直接打印 8
    5. fn函数直接调用,相当于同步任务,直接打印 12 ;
    6. 遇到console,相当于同步任务,直接打印 9 ;
    7. 遇到promise.then 属于微任务队列,但是它在promise实例化的时候被调用了,所以它会在微任务队列的最前面执行;
    8. 到这里主线程里面就没有任何可以执行到东西了,下面开始走微任务队列(microtask):
      1. 由于promise.then被提前调用了,所以它会先执行,打印 10 ;
      2. 微队列(microtask)里面还有一个,就是上面的process.nextTick,执行它,打印 2,这个时候发现它有一个setTimeout,放到宏队列(macrotask);
      3. setTimeout在一开始的时候被放了进去,所以先执行它,打印 4, 发现它里面有promise被实例化,直接执行,打印 5
      4. 下一个要走的就是promise里面的setTimeout,打印 7 ;
      5. 还剩最后一个setTimeout,就是process.nextTick里面的,打印 3
      6. 到这里主线程、宏队列(macrotask)、微队列(microtask)就全都跑完了,在全部跑完的时候
    9. requesIdleCallback会在当前浏览器空闲时期去依次执行,在整个过程当中你可能添加了多个requestIdleCallback,但是都不会执行,只会在空闲时期,去依次根据调用的顺序就执行。打印 6 ;
  • 相关阅读:
    开发新手最容易犯的50个 Ruby on Rails 错误(1)
    Spring Data Redis 让 NoSQL 快如闪电(2)
    为什么每个程序员都应该懂点前端知识?
    如何在 Flickr 上找到又酷,又有趣,且版权自由的照片?
    微服务扩展新途径:Messaging
    为什么现代企业无法真正实现组合式监控?
    开发者和程序员需要关注的42个播客
    战略性情绪分析的5大数据来源
    Spring Data Redis 让 NoSQL 快如闪电 (1)
    对抗告警疲劳的8种方法
  • 原文地址:https://www.cnblogs.com/ningxin/p/15957941.html
Copyright © 2020-2023  润新知