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

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

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,332评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,508评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,812评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,607评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,728评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,919评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,071评论 3 410
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,802评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,256评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,576评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,712评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,389评论 4 332
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,032评论 3 316
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,798评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,026评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,473评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,606评论 2 350

推荐阅读更多精彩内容