zoukankan      html  css  js  c++  java
  • Event Loop

    Event Loop

    为什么JavaScript是单线程

    其最初的用途来决定的:与浏览器交互。
    试想一下 如果javascript是多线程的,那么当两个线程同时对dom进行一项操作,例如一个向其添加事件,而另一个删除了这个dom,此时该如何处理呢?因此,为了保证不会 发生类似于这个例子中的情景,javascript选择只用一个主线程来执行代码,这样就保证了程序执行的一致性。
    一切javascript版的"多线程"都是用单线程模拟出来的,一切javascript多线程都是纸老虎!

    宏任务(macro-task)和微任务(micro-task)

    JavaScript的事件分两种,宏任务(macro-task)和微任务(micro-task)

    • 宏任务:包括整体代码script,setTimeout,setInterval, setImmediate(node独有), I/O, UI rendering

    • 微任务:Promise.then(非new Promise),process.nextTick(node独有),Object.observe(废弃), MutationObserver

    • async函数使用await以后得所有语句会被放入一个回调函数中,放入微任务中

    • 2个微任务的优先级,promise高于async

    浏览器js引擎事件循环机制

    事件的执行顺序的基础是 先执行宏任务,后执行微任务
    任务可以有同步任务和异步任务,同步和异步任务分别进入不同的执行"场所"

    1. 同步的进入主线程;
    2. 异步的进入Event Table并注册函数; 异步事件完成后,会将回调函数放入Event Queue中(宏任务和微任务是不同的Event Queue);
    3. 主线程内的任务执行完毕为空,会去Event Queue读取对应的函数。
    4. 回调函数中可能还会包含不同的任务,因此会循环执行上述操作。
    • 宏任务同步代码 => 微任务(队列) => 宏任务(队列)
      => 微任务(队列) ...
    setTimeout(function() {
        console.log('setTimeout');
    },1000)
    
    new Promise(function(resolve) {
        console.log('promise');
    }).then(function() {
        console.log('then');
    })
    
    console.log('console');
    
    • 首先setTimeout,放入Event Table中,1秒后将回调函数放入宏任务的Event Queue
    • new Promise 同步代码,立即执行console.log('promise'),然后看到微任务then,因此将其放入微任务的Event Queue
    • 接下来执行同步代码console.log('console')
    • 主线程的宏任务执行完毕,接下来要执行微任务,因此会执行Promise.then,到此,第一轮事件循环执行完毕
    • 第二轮事件循环开始,先执行宏任务(的事件队列),即setTimeout的回调函数,然后查找是否有微任务,没有,事件循环结束

    事件循环,先执行宏任务,其中同步任务立即执行,异步任务,加载到对应的的Event Queue,所有同步宏任务执行完毕后,如果发现微任务的Event Queue中有未执行的任务,会先执行其中的任务,这样算是完成了一次事件循环。接下来查看宏任务的Event Queue中是否有未执行的任务,有的话,就开始第二轮事件循环,依此类推。

    来至于谷友的一到面试题

    <script>
    async function async1() {
      console.log('async1 start');
      await async2();
      console.log('async1 end');
    }
    async function async2() {
      console.log('async2');
    }
    console.log('script start');
    setTimeout(function() {
        console.log('setTimeout');
    }, 0);
    async1();
    new Promise(function(resolve) {
        console.log('promise1');
        resolve();
      }).then(function() {
        console.log('promise2');
      });
    console.log('script end');
    </script>
    
    // script start => async1 start => async2 => promise1 => script end => promise2 => async1 end => setTimeout
    
    1. 整个代码块作为一个宏任务,进入主线程

    2. 看到函数申明但没有执行,遇到函数console.log执行,输出script start

    3. 遇到setTimeout() ,把它的回调函数放入宏任务(setTimeout1)。

      宏任务 微任务
      setTimeout1
    4. 遇到执行async1(), 进入async的执行上下文之后,遇到console.log输出async1 start

    5. 然后遇到await async2(),由于()的优先级高,所有先执行async2(),进入async2()的执行上下文。

    6. 看到console.log输出async2,之后没有返回值,结束函数,返回undefined,返回async1的执行上下文的await undefined,由于async函数使用await后得语句会被放入一个回调函数中,所以把下面的放入微任务中。

      宏任务 微任务
      setTimeout1 async1=> awati 后面的语句
    7. 结束async1,返回全局上下文,遇到Promise构造函数,里面的函数立马执行, 输出promise1, 之后的回调函数进入微任务

      宏任务 微任务
      setTimeout1 async1=> awati 后面的语句
      new Promise() => 后的then
    8. 执行完Promise(),遇到console.log,输出script end,这里一个宏任务代码块执行完毕。

    9. 在主线程执行的过程中,事件触发线程会一直监听异步事件,当异步事件处理完成后,把它的回调函数放入事件队列,等待执行。

    10. 主线程现在空闲下来后,执行事件队列中的微任务,然后继续向下执行,遇到new Promise()后面的回调函数,执行代码,输出promise2(这里2个微任务的优先级,promise高于async)。

    11. 看到async1中await后面的回调函数,执行代码,输出async1 end

      宏任务 微任务
      setTimeout1
    12. 此时微任务中的队列为空,开始执行队列中的宏任务,进入一个新的代码块。遇到console.log,输出setTimeout


    原文链接:https://www.jianshu.com/p/de7aba994523

  • 相关阅读:
    Windows下安装并设置Redis
    Android平台相机接口的应用
    Struts(十五):主题
    Struts(十四):通用标签-form表单
    Struts(十三):通用标签
    Struts(十二):异常处理:exception-mapping元素
    Struts(十一):OGNL表达式(二)
    Struts(十):OGNL表达式(一)
    A*算法
    Centos:如何查找安装的jdk的目录
  • 原文地址:https://www.cnblogs.com/topyang/p/11411571.html
Copyright © 2011-2022 走看看