• 阿桐
    2019-09-17
    买过不少专栏,每一篇都紧跟并且会反复看的目前只有这一个。一方面懒另一方面是好的系统性的学习资料不多,所以以前很少关注偏底层原理性的东西,所以这个专栏学习起来是既收获满满有时也不乏一额头问号。

    这里有 2 个问题想向老师请教,希望老师百忙之中能抽空解答一下,多谢多谢。

    1、之前讲过,在循环系统的一个循环中,先从消息队列头部取出一个任务执行,该任务执行完后,再去延迟队列中找到所有的过期任务依次执行完。那前面这句话和本篇文章的这句话好像有矛盾:"先从多个消息队列中选出一个最老的任务,这个任务称为 oldestTask"

    2、”通常情况下,在当前宏任务中的 JavaScript 快执行完成时,也就在 JavaScript 引擎准备退出全局执行上下文并清空调用栈的时候,JavaScript 引擎会检查全局执行上下文中的微任务队列,然后按照顺序执行队列中的微任务。“
    在页面生存周期内,全局执行上下文只有一份并且会一直存在调用栈中,只有当页面被关闭的时候全局执行上下文才会消失。页面都快关闭了,把全局执行上下文中的微任务队列中的任务都执行一遍,好像也没啥意义。系统应该不会做没有意义的事情,所以应该是我对全局执行上下文的某处理解有问题,但我又自查不到。
    展开

    作者回复:

    非常高兴你能提出这些问题。

    你有这两个疑问很正常,说明你看得很仔细,之所以你会感到疑惑,主要是我在写作过程中偷懒了。

    我先来解答你的第一个问题:
    第一段话是WHATWG标准定义的,在WHATWG规范,定义了在主线程的循环系统中,可以有多个消息队列,比如鼠标事件的队列,IO完成消息队列,渲染任务队列,并且可以给这些消息队列排优先级。

    但是在浏览器实现的过程中,目前只有一个消息队列,和一个延迟执行队列。
    一个是规范,一个是实现,主要我没有在文中强调这点,所以你会产生的这样的疑问。

    关于第二个问题解释起来就比较复杂了,涉及到来了V8是怎么执行的了,专栏中的"全局执行上下文"我没有深入分析。所以我偷懒了,把两个稍微有点不同的概念都称为了“全局执行上下文”,要解释清楚这个问题还要牵涉到V8的一个底层逻辑,既然你提出来了,那我就打算在课程结束后,通过加餐的形式来开一讲,讲清楚了这个还能额外地理解 Realm 概念。

     4
     29
  • man-moonth
    2019-09-14
    错误和缺失之处烦请老师指正:
    1. 执行`p0 = new Promise(executor)`,立即调用`executor()`。依次打印`1`和`rand`,根据`rand > 0.5`判断执行`resolve()`还是`reject()`,分别决定了p0的状态为fufilled(成功)还是rejected(失败)。
    2. 继续往下执行`p1 = p0.then()`、`p3 = p1.then()`、`p4 = p3.then()`、`p4.catch()`,`p0.then()`、`p1.then()`、`p3.then()`、`p4.catch()`等依次推入微任务队列,p1、p3、p4的状态变为pending(初始状态)。此处p4添加了`catch()`方法,若p4也有`then()`方法,那么推入队列的就是`p4.then().catch()`。
    3. 执行`console.log(2)`。宏任务执行完毕。
    4. 从微任务队列中取出`p0.then()`。如果p0的状态为fufilled,那么执行`p0.then()`:打印`succeed-1`,然后执行`new Promise(executor)`,完毕后p1的状态转为fufilled/rejected;如果p0的状态为rejected,则不执行`p0.then()`,p1的状态置为rejected。
    5. 继续从微任务队列取出`p1.then()`、`p3.then()`,他们的处理方式与第3步同理。
    6. 取出`p4.catch()`,如果p4的状态为rejected,那么执行`p4.catch()`,否则啥也不做。结束。
    展开
     1
     4
  • 一步
    2019-09-15
    老师,我看文章的图展示,微任务队列只存在全局执行上下文中吗? 如果一个微任务是在一个函数执行上下文中产生了,也会保存到全局执行上下文中的微队列中吗?
     1
     3
  • ytd
    2019-09-14
    执行过程:
      从第10行开始:
      1,创建promise赋值,打印1 rand
      2,执行log语句,打印2
      3,如果rand > 0.5,promise被resolve,打印success,并返回新的promse赋值
        然后重复类似步骤1、3、4
      4,否则如果rand <= 0.5 promise reject,然后p1、p2、p3、p4都分别被赋值为一个新的被reject的promise,最后在p4.catch中reject状态被捕获,打印error
    展开
     1
     3
  • splm
    2019-10-16
    前面的课程举过说过,正常任务会被放在消息队列中,延时任务会放在延时消息队列中,还举过一段代码,任务循环会不断的从消息队列中取任务,并执行,也会不断的判断延时任务是否到期需要执行。但在这节课里面却说延迟任务会追加到消息队列末尾,听说去就像普通任务和延迟任务都在一起,只是延迟任务被追加到末尾。究竟有几个消息队列,普通和延迟队列是真实存在还是只是概念区分,实际两种任务都保存在一块。
    
     2
  • Andy Jiang
    2019-10-15
    之前讲过,在循环系统的一个循环中,先从消息队列头部取出一个任务执行,该任务执行完后,再去延迟队列中找到所有的过期任务依次执行完。消息队列头部取出的任务执行完毕后,会先检查微任务队列么?检查微任务队列,然后再去延迟队列中找过期任务执行?
     1
     2
  • 瞧,这个人
    2019-09-17
    提问: Mutation Event的回调 是同步的吗?如果是同步的,引擎是怎么做到的?
    同步代码执行的时候,还能插入其他代码(mutation 回调)?

    作者回复: 要理解这个就得讲观察者模式了,不过展开又是一篇文章,我到时候加餐的时候再来结合观察者模式来讲这个。

     1
     2
  • locke
    2019-10-17
    文中“第一种是把异步回调函数封装成一个宏任务,添加到消息队列尾部”,setTimeout--不是添加到消息队列尾部吧,不应该延迟队列吗?
     1
     1
  • splm
    2019-10-16
    延迟任务队列是真实存在的还是只是一个定义,实际上就是追加普通宏任务队列后面或者被添加到微任务中的任务集?
     1
     1
  • 任振鹏
    2019-10-14
    调用栈:全局执行上下文 -> executor函数执行上下文
    先执行宏任务队列:
      先打印1, rand,返回一个promiss,executor函数执行上下文出栈,执行全局上下文的微任务队列加入pomiss在打印2
    然后执行微任务队列:遇到reject 打印error 结束, 否则:打印succeed-1,然后executor函数执行上下文进栈,继续上面步骤。直至reject结束
    不知道上面的分析对不对, 希望老师解答下。
    
     1
  • 小智
    2019-09-19
    反复读了几次,还是有很多疑问,不同于最初的几篇文章,这里的理论偏多,希望能有更多的案例结合理论分析,才能进一步验证心中理解的

    作者回复: 嗯。后面有疑问正常,理论偏多,而且篇幅之间的依赖性比较强。可以把你的问题列出来,答疑的时候我会结合实际列子来分析。

     1
     1
  • liu_xm
    2019-09-16
    您好,有些疑问。
    1.微任务是在v8引擎中添加的,是不是在ie和firefox中都不存在呢?
    2.再说dom变化多次后再出发muation observer。这个多次是如何定义和谁来控制的呀?
     1
     1
  • AMIR
    2019-12-24
    老师,有个问题请教一下,第一个问题,延时队列如果存在,那么为什么两个setTimeOut中被加入那么多其他任务,不应该是最多执行一个,然后判断延时队列是否有到期de,然后就执行嘛?怎么会添加到尾巴上
    
    
  • Chao
    2019-12-24
    宏任务并未出现在规范内。
    
    
  • coder
    2019-12-22
    对于文中一处有疑虑:

    “第一种是把异步回调函数封装成一个宏任务,添加到消息队列尾部,当循环系统执行到该任务的时候执行回调函数。这种比较好理解,我们前面介绍的 setTimeout 和 XMLHttpRequest 的回调函数都是通过这种方式来实现的。”

    第16讲提到了,setTimeout里的延迟任务,是存在一个延迟队列中的。我看精选留言部分老师的回答,提到这个延迟队列实际上是一个hashmap,那么setTimeout的实现还是加到队尾,等到前面的都出队,才执行到这个任务的吗?
    展开

    作者回复: setTimeout因为是定时任务,设定的时间间隔没有到是不会执行,由此需要一个单独的模块来保存定时器的消息,你可以通过该模块取出到期的任务,我们把这个模块叫延时队列,Chrome内部用了个hashmap保存数据,然后又写了取出到期的任务的策略!

    通常情况下,是当渲染主线程在执行完一个正常的任务之后,再判断该模块中是否有到期的任务,如果有取出来执行!

     1
    
  • 夏了夏天
    2019-12-18
    老师,我有个疑惑,主线程读取消息队列里的任务的时机是「系统调用栈」的任务执行完毕时还是「JS的调用栈」执行完毕时?

    作者回复: js只不是任务的一个过程,这里讲的都是C++层面的,所以也可以说是系统调用栈。

    具体的你可以看看加餐5

    
    
  • 海之蓝心
    2019-12-06
    老师,文中提到,微任务早于宏任务执行?
    我的理解:当前宏任务执行完退出之前,再执行微任务列表的任务。
    还有当前宏任务又有延迟列表任务,又有微任务,应该怎么执行?
    求解惑??
    
    
  • 凭实力写bug
    2019-11-02
    执行流程很清楚,也知道了几个队列的关系,看完唯一迷惑的是那里算一个任务,对着performance执行流程就很清晰了

    作者回复: 后续有补充内容

    
    
  • Cris
    2019-10-04
    这篇赞
    
    
  • Cris
    2019-10-04
    先从多个消息队列中选出一个最老的任务,这个任务称为 oldestTask
    老师一次事件循环只执行多个消息队列里最老的任务(即一次事件循环只执行一个任务)还是取每个消息列表里最老的任务(即一次事件循环会执行多个任务)?
    
    
我们在线,来聊聊吧