javascript的事件循环机制(Event Loop)

执行栈

先来看一下《JavaScript高级程序设计第三版》中有一段关于执行函数与执行栈的描述。

执行环境(execution context,为简单起见,有时也称为“环境”)是 JavaScript 中最为重要的一个概念。执行环境定义了变量或函数有权访问的其他数据,决定了它们各自的行为。每个执行环境都有一个 与之关联的变量对象(variable object),环境中定义的所有变量和函数都保存在这个对象中。
每个函数都有自己的执行环境。当执行流进入一个函数时,函数的环境就会被推入一个环境栈中。 而在函数执行之后,栈将其环境弹出,把控制权返回给之前的执行环境。ECMAScript 程序中的执行流正是由这个方便的机制控制着。

接下来看一段代码,以及这段代码在chrome中的运行过程。

function foo(b) {
  var a = 10;
  return a + b + 11;
}
function bar(x) {
  var y = 3;
  return foo(x * y);
}
console.log(bar(7)); // 返回 42
执行栈

我们可以看到以上js代码执行时,首先调用了bar函数,bar函数先进入栈,在bar函数中又调用了foo函数,foo函数进栈,然后根据栈先进后出的原则,foo函数执行完毕后先出栈,然后bar函数出栈。

任务队列(Task Queue)与事件循环(Event Loop)

但是执行栈并不是遇到什么代码都立即执行的,比如ajax

我们在平时开发中经常会用到ajaxajax回调函数里的代码是异步执行的。以下代码会先打印1,然后打印2。

$.ajax({
  type:'post',
  url:'xxx.com',
  data:{xx:'xx'},
  success(result) {
    console.log(2)
  },
});
console.log(1)

这是因为js是单线程的,执行栈不可能一直等待ajax的回调函数执行完毕之后再执行下面的代码,这样的话如果网络不通畅或者很耗时那么下面的代码会被阻塞很久。所以js在遇到ajax这样的异步代码时会将其加入到一个任务队列中,执行栈处理完任务后处于空闲状态时就会取出任务队列中最前面的任务进入执行栈。
检查执行栈是否空闲,取出任务队列中的任务并执行的过程是不断循环的,这个过程就是事件循环。

[图片上传失败...(image-d64c57-1584078574091)]

上图是MDN上关于并发模型与事件循环的可视化描述,stack就是执行栈,queue就是任务队列。

微任务(microtask)和宏任务(macrotask)

在js中除了ajax外,其实还有很多我们熟知的异步代码,比如setTimeout,setInterval,Promise等,同为异步代码,但是还是有一些区别的。

这些异步代码加入到任务队列之后会分为 微任务宏任务

  • 微任务: process.nextTick, promiseMutationObserver
  • 宏任务: 主代码块,MessageChannel, setTimeoutsetIntervalsetImmediatenetwork IOUI render

事件循环步骤

事件循环步骤

从上图中可以了解,事件循环中,宏任务和微任务是按照一定步骤执行的。

  1. 首先检查队列中是否有等待执行的宏任务,有则执行一个宏任务。

  2. 然后检查队列中是否有等待执行的微任务,有则执行队列中所有微任务。

  3. 判断是否需要渲染UI,如果需要则渲染UI并 回到第1步 ,不需要则直接回到第1步,检查队列中是否有等待执行的宏任务。

console.log('start')
setTimeout(() => {
  console.log('setTimeout callback');
});
Promise.resolve().then(function() {
  console.log('promise then callback')
})

按照上面事件循环的步骤,应该先进行第一个宏任务主代码块的执行,打印start,遇到setTimeout将其加入到 宏任务,遇到promise.then将其加入到 微任务,此时第一个宏任务主代码块已经执行完毕,接下来检查队列中的微任务并执行,所以打印 'promise then callback' ,执行完所有微任务后,重新检查任务队列发现宏任务setTimeout然后执行,打印 'setTimeout callback'。

在chrome调试代码运行步骤结果符合预期。

chrome调试代码

我们再来看一个稍微复杂点的例子

console.log('start')
setTimeout(() => {
  console.log('setTimeout callback');
  Promise.resolve().then(function() {
    console.log('promise2 in setTimeout')
    setTimeout(()=>{
      console.log('setTimeout2 in Promise2');
    })
  })
});
Promise.resolve().then(function() {
  console.log('promise then callback')
})

chrome运行结果

上面的代码其实一共进行了 3次 事件循环

  • 第1次 循环打印 'start' 与 'promise then callback'。
  • 第2次 循环打印了 'setTimeout callback' 与 'promise2 in setTimeout'。
  • 第3次 循环打印了 'setTimeout2 in Promise2'。

原文地址

参考


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

推荐阅读更多精彩内容