美文网首页
事件循环机制(消息队列)

事件循环机制(消息队列)

作者: EO_eaf6 | 来源:发表于2020-08-28 11:03 被阅读0次

JS是单线程语言,因此同一时刻只能执行一行代码,因此,在遇到异步任务时,就出现了事件循环机制来处理异步任务。

任务队列:同步任务队列和异步任务队列

异步任务队列:(根据任务源分为)宏任务和微任务

宏任务:浏览器或者Node环境实现的

image.png

宏任务队列:setTimeout,setInterval,I/O事件(鼠标点击事件、console.log()等),setImmediate(一个EventLoop执行完执行),requestAnimationFrame(页面重绘前的操作),页面重绘

微任务:js引擎自身提供的

image.png

微任务队列:Promise,MutationObserver(浏览器,DOM发生变化时立即放入微任务队列(如属性变化),多次修改也只触发一次),process.nextTick(NodeJS)

setTimeout和setImmediate:

//setTimeout在设置延迟后执行,setImmediate在一个Event Loop结束时调用
setTimeout(_ => console.log('setTimeout'))
setImmediate(_ => console.log('setImmediate'))
//在主线程直接执行这两个操作,输出顺序是不一定的

为了保证setTimeout调用在setImmediate之前,将一个事件循环过程加长,保证setImmediate的晚执行,比如添加一个while循环

MutationObserver:监听DOM变化,在最后一个DOM变化后调用,DOM多次改变,只调用一次

function domChange(){
       document.body.removeChild()
       document.body.appendChild()
       document.body.appendChild()
}
new MutationObserver(()=>{
       console.log("MutationObserver")
})
//domChange调用时输出1次MutationObserver

requestAnimationFrame:页面重绘前调用

1、new MutationObserver(function () {
  console.log('mutate');
}).observe(outer, {
  attributes: true,
});//注册微任务
2、outer.setAttribute('data-random', Math.random());//执行调用1
3、requestAnimationFrame(()=>{
    console.log("requestAnimationFrame")
  })//注册宏任务
//mutate
//requestAnimationFrame

处理过程:所有的任务可以分为同步任务和异步任务,所有同步任务在主线栈中执行,代码按顺序执行,同步代码立即执行,遇到异步代码,注册到宏任务队列或微任务队列,
同步代码执行完毕后检查微任务队列,执行完微任务队列,检查执行宏任务队列。

当触发事件时存在冒泡现象时:

通过addListener添加的click(手动点击DOM触发):同步代码-》微任务-》(冒泡)同步代码-》微任务-》两次宏任务

<div class="outer">
    <div class="inner" style="border: 2px red solid">div</div>
  </div>
<script>
var outer = document.querySelector('.outer');
var inner = document.querySelector('.inner');

new MutationObserver(function () {
  console.log('mutate');
}).observe(outer, {
  attributes: true,
});//注册微任务
function onClick() {
  console.log('click');

  setTimeout(function () {
    console.log('timeout');
  },0);
  
  Promise.resolve().then(function () {
    console.log('promise');
  });

  outer.setAttribute('data-random', Math.random());
  requestAnimationFrame(()=>{
    console.log("requestAnimationFrame")
  })
}
inner.addEventListener('click', onClick);
outer.addEventListener('click', onClick);
</script>
//执行结果
click
promise
mutate
click
promise
mutate
requestAnimationFrame//两次click执行的宏任务
requestAnimationFrame
timeout//两次click执行的宏任务
timeout
点击内层div调用onClick方法,执行同步代码,微任务,进入冒泡再次调用onClick,执行同步代码,微任务,两次宏任务
这里有两个问题不太明白:
(1)MutationObserver什么时候压入微任务队列的
(2)requestAnimationFrame和timeout为什么是连续输出而不是交叉输出

页面加载时直接调用(node)inner.click():同步代码-》(冒泡)同步代码-》微任务-》宏任务,应该不是一个I/O事件
inner.click()相当于一个同步代码执行,因此在microTask之前执行

<script>
var outer = document.querySelector('.outer');
var inner = document.querySelector('.inner');

new MutationObserver(function () {
  console.log('mutate');
}).observe(outer, {
  attributes: true,
});//注册微任务
function onClick() {
  console.log('click');

  setTimeout(function () {
    console.log('timeout');
  },0);
  
  Promise.resolve().then(function () {
    console.log('promise');
  });

  outer.setAttribute('data-random', Math.random());
  requestAnimationFrame(()=>{
    console.log("requestAnimationFrame")
  })
}
inner.addEventListener('click', onClick);
outer.addEventListener('click', onClick);
inner.click();//第一次页面加载时直接调用click方法,非手动触发,此时输出结果
click
click
promise
mutate
promise
requestAnimationFrame
requestAnimationFrame
timeout
timeout
</script>
这里有个问题不太明白:
为什么mutate只输出了一次

new Promise异步操作的输出顺序

new Promise(resolve => {
  resolve();//A
}).then(() => {
    new Promise(resolve => {
      resolve();//B1
    })
      .then(()=>{new Promise((resolve) => {
          resolve()
        })
          .then(() => {new Promise((resolve)=>{
            resolve()
          })
          .then(()=>{
            console.log(1) 
          })

        }
      )
          .then(() => { console.log(11) })
      })
      .then(() => { console.log(222); });
  })
  .then(() => {
    new Promise((resolve => {
      resolve()
    }))
      .then(() => {
        new Promise((resolve) => {
          resolve()
        })
          .then(() => { console.log(333) })
          .then(() => { console.log(444) })
      })
      .then(() => {
        console.log(555);
      })
  })
  .then(() => {
    console.log(666);
  })
//输出结果:
222
666
1
11
333
555
444
结果与操作系的计算速度等有关
参考自:[https://stackoverflow.com/questions/58270410/how-to-understand-this-promise-execution-order](https://stackoverflow.com/questions/58270410/how-to-understand-this-promise-execution-order)

所有参考自:https://www.cnblogs.com/yugege/p/9598265.html
https://jakearchibald.com/2015/tasks-microtasks-queues-and-schedules/#level-1-bossfight
https://juejin.im/post/6844903657264136200

相关文章

  • 成长(10/2000)——面试题合集7

    事件循环机制event-loop 事件循环机制由三部分组成:调用栈、消息队列和微任务队列。 event-loop开...

  • 事件循环机制(消息队列)

    JS是单线程语言,因此同一时刻只能执行一行代码,因此,在遇到异步任务时,就出现了事件循环机制来处理异步任务。 任务...

  • 《浏览器工作原理与实践》学习笔记(四)

    消息队列和事件循环 要想在线程运行过程中,能接收并执行新的任务,就需要采用事件循环机制。 事件循环机制:相比于线性...

  • 20211021

    1、js里的事件循环机制(event loop)答:js事件循环中有异步队列有两种:宏任务队列(macro)和微任...

  • 事件驱动,IO多路复用

    事件驱动 有一个事件(消息)队列; 鼠标按下时,往这个队列中增加一个点击事件(消息); 有个循环,不断从队列取出事...

  • Dart线程模型及异常捕获

    单线程 以类似JS的,单线程中是以消息循环机制来运行的。包含一个微任务队列 ,和一个事件队列。微任务队列的执行优先...

  • 消息队列和事件循环

    消息队列 浏览器页面是由消息队列和事件循环系统来驱动的。 如果有一些确定好的任务,可以使用一个单线程来按照顺序处理...

  • Android中的Handler

    Handler 是一种消息处理机制。我们将消息放在队列中,Looper会循环从队列中获取消息,然后通过handle...

  • Android-消息机制

    整体机制 一共有四个角色,Handler消息处理者、Looper消息循环、MessageQueue消息队列、Mes...

  • 电话笔记 | RIL整体框架事件处理介绍

    一、 启动事件循环处理eventLoop工作线程 建立多路I/O驱动机制的消息队列,用来接收上层发出的命令以及往M...

网友评论

      本文标题:事件循环机制(消息队列)

      本文链接:https://www.haomeiwen.com/subject/jlghsktx.html