Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

从Promise来看JavaScript中的Event Loop、Tasks和Microtasks #21

Open
creeperyang opened this issue May 5, 2016 · 53 comments

Comments

Projects
None yet
@creeperyang
Copy link
Owner

commented May 5, 2016

看到过下面这样一道题:

(function test() {
    setTimeout(function() {console.log(4)}, 0);
    new Promise(function executor(resolve) {
        console.log(1);
        for( var i=0 ; i<10000 ; i++ ) {
            i == 9999 && resolve();
        }
        console.log(2);
    }).then(function() {
        console.log(5);
    });
    console.log(3);
})()

为什么输出结果是1,2,3,5,4而非1,2,3,4,5

比较难回答,但我们可以首先说一说可以从输出结果反推出的结论:

  1. Promise.then是异步执行的,而创建Promise实例(executor)是同步执行的。
  2. setTimeout的异步和Promise.then的异步看起来 “不太一样” ——至少是不在同一个队列中。

相关规范摘录

在解答问题前,我们必须先去了解相关的知识。(这部分相当枯燥,想看结论的同学可以跳到最后即可。)

Promise/A+规范

要想找到原因,最自然的做法就是去看规范。我们首先去看看Promise的规范

摘录promise.then相关的部分如下:

promise.then(onFulfilled, onRejected)

2.2.4 onFulfilled or onRejected must not be called until the execution context stack contains only platform code. [3.1].

Here “platform code” means engine, environment, and promise implementation code. In practice, this requirement ensures that onFulfilled and onRejected execute asynchronously, after the event loop turn in which then is called, and with a fresh stack. This can be implemented with either a “macro-task” mechanism such as setTimeout or setImmediate, or with a “micro-task” mechanism such as MutationObserver or process.nextTick. Since the promise implementation is considered platform code, it may itself contain a task-scheduling queue or “trampoline” in which the handlers are called.

规范要求,onFulfilled必须在 执行上下文栈(execution context stack) 只包含 平台代码(platform code) 后才能执行。平台代码指 引擎,环境,Promise实现代码。实践上来说,这个要求保证了onFulfilled的异步执行(以全新的栈),在then被调用的这个事件循环之后。

规范的实现可以通过 macro-task 机制,比如setTimeoutsetImmediate,或者 micro-task 机制,比如MutationObserver或者process.nextTick。因为promise的实现被认为是平台代码,所以可以自己包涵一个task-scheduling队列或者trampoline

通过对规范的翻译和解读,我们可以确定的是promise.then是异步的,但它的实现又是平台相关的。要继续解答我们的疑问,必须理解下面几个概念:

  1. Event Loop,应该算是一个前置的概念,理解它才能理解浏览器的异步工作流程。
  2. macro-task 机制和 micro-task 机制,这组概念很新,之前根本没听过,但却是解决问题的核心。

Event Loop规范

HTML5规范里有Event loops这一章节(读起来比较晦涩,只关注相关部分即可)。

  1. 每个浏览器环境,至多有一个event loop。
  2. 一个event loop可以有1个或多个task queue。
  3. 一个task queue是一列有序的task,用来做以下工作:Events task,Parsing task, Callbacks task, Using a resource task, Reacting to DOM manipulation task等。

每个task都有自己相关的document,比如一个task在某个element的上下文中进入队列,那么它的document就是这个element的document。

每个task定义时都有一个task source,从同一个task source来的task必须放到同一个task queue,从不同源来的则被添加到不同队列。

每个(task source对应的)task queue都保证自己队列的先进先出的执行顺序,但event loop的每个turn,是由浏览器决定从哪个task source挑选task。这允许浏览器为不同的task source设置不同的优先级,比如为用户交互设置更高优先级来使用户感觉流畅。

Jobs and Job Queues规范

本来应该接着上面Event Loop的话题继续深入,讲macro-task和micro-task,但先不急,我们跳到ES2015规范,看看Jobs and Job Queues这一新增的概念,它有点类似于上面提到的task queue

一个Job Queue是一个先进先出的队列。一个ECMAScript实现必须至少包含以下两个Job Queue

Name Purpose
ScriptJobs Jobs that validate and evaluate ECMAScript Script and Module source text. See clauses 10 and 15.
PromiseJobs Jobs that are responses to the settlement of a Promise (see 25.4).

单个Job Queue中的PendingJob总是按序(先进先出)执行,但多个Job Queue可能会交错执行。

跟随PromiseJobs到25.4章节,可以看到PerformPromiseThen ( promise, onFulfilled, onRejected, resultCapability )

这里我们看到,promise.then的执行其实是向PromiseJobs添加Job。

event loop怎么处理tasks和microtasks?

好了,现在可以让我们真正来深入task(macro-task)和micro-task。

认真说,规范并没有包括macro-task 和 micro-task这部分概念的描述,但阅读一些大神的博文以及从规范相关概念推测,以下所提到的在我看来,是合理的解释。但是请看文章的同学辩证和批判地看。

首先,micro-task在ES2015规范中称为Job。 其次,macro-task代指task。

哇,所以我们可以结合前面的规范,来讲一讲Event Loop(事件循环)是怎么来处理task和microtask的了。

  1. 每个线程有自己的事件循环,所以每个web worker有自己的,所以它才可以独立执行。然而,所有同属一个origin的windows共享一个事件循环,所以它们可以同步交流。
  2. 事件循环不间断在跑,执行任何进入队列的task。
  3. 一个事件循环可以有多个task source,每个task source保证自己的任务列表的执行顺序,但由浏览器在(事件循环的)每轮中挑选某个task source的task。
  4. tasks are scheduled,所以浏览器可以从内部到JS/DOM,保证动作按序发生。在tasks之间,浏览器可能会render updates。从鼠标点击到事件回调需要schedule task,解析html,setTimeout这些都需要。
  5. microtasks are scheduled,经常是为需要直接在当前脚本执行完后立即发生的事,比如async某些动作但不必承担新开task的弊端。microtask queue在回调之后执行,只要没有其它JS在执行中,并且在每个task的结尾。microtask中添加的microtask也被添加到microtask queue的末尾并处理。microtask包括mutation observer callbackspromise callbacks

结论

定位到开头的题目,流程如下:

  1. 当前task运行,执行代码。首先setTimeout的callback被添加到tasks queue中;
  2. 实例化promise,输出 1; promise resolved;输出 2;
  3. promise.then的callback被添加到microtasks queue中;
  4. 输出 3;
  5. 已到当前task的end,执行microtasks,输出 5;
  6. 执行下一个task,输出4
@heartAndRain

This comment has been minimized.

Copy link

commented Oct 31, 2016

有一个问题就是,I/O, UI render 究竟是不是当做一个task来执行,看有的文章说是,有的说是在每个task之间进行的

@creeperyang

This comment has been minimized.

Copy link
Owner Author

commented Oct 31, 2016

@cendylee 就我看到的资料来说,好像是在task之间进行的。如果有更详细可靠的资料进行补充或更正当然更好。

@hyj1991

This comment has been minimized.

Copy link

commented Feb 8, 2017

Promise的then原型方法注册的回调确实是在microtask中注册执行的,但是我很好奇,node实现的process.nextTick,看源码似乎并不是由microtask驱动的,为啥网上到处都说process.nextTick也是属于microtask的一部分呢?

@creeperyang

This comment has been minimized.

Copy link
Owner Author

commented Feb 13, 2017

@hyj1991 从源码来看,process.nextTick 属于 microtask。

https://github.com/nodejs/node/blob/v7.x/src/node.cc#L4381

inline int Start(Isolate* isolate, IsolateData* isolate_data,
                 int argc, const char* const* argv,
                 int exec_argc, const char* const* exec_argv) {

...
  {
    Environment::AsyncCallbackScope callback_scope(&env);
    LoadEnvironment(&env);
  }

https://github.com/nodejs/node/blob/v7.x/src/node.cc#L3406

void LoadEnvironment(Environment* env) {
...
  Local<String> script_name = FIXED_ONE_BYTE_STRING(env->isolate(),
                                                    "bootstrap_node.js");
  Local<Value> f_value = ExecuteString(env, MainSource(env), script_name);

https://github.com/nodejs/node/blob/v7.x/lib/internal/bootstrap_node.js#L12

function startup() {
...
NativeModule.require('internal/process/next_tick').setup();

https://github.com/nodejs/node/blob/v7.x/lib/internal/process/next_tick.js#L49

  function scheduleMicrotasks() {
    if (microtasksScheduled)
      return;

    nextTickQueue.push({
      callback: runMicrotasksCallback,
      domain: null
    });

    tickInfo[kLength]++;
    microtasksScheduled = true;
  }

  ...

  function nextTick(callback) {
    if (typeof callback !== 'function')
      throw new TypeError('callback is not a function');
    // on the way out, don't bother. it won't get fired anyway.
    if (process._exiting)
      return;

    var args;
    if (arguments.length > 1) {
      args = new Array(arguments.length - 1);
      for (var i = 1; i < arguments.length; i++)
        args[i - 1] = arguments[i];
    }

    nextTickQueue.push({
      callback,
      domain: process.domain || null,
      args
    });
    tickInfo[kLength]++;
  }

可以看到,安排microtask就是向nextTickQueue队列压入callback,而nextTick同样是向nextTickQueue队列压入callback,可见,process.nextTick 属于 microtask

20170629 更新代码路径

@Ma63d

This comment has been minimized.

Copy link

commented Mar 3, 2017

很清晰的文章!
我也是有同样的疑惑:到底UI render是在哪执行的?
博主您给出的HTML规范链接里面的这部分内容我反复读了,但是Processing model的第5点Update the rendering之后说的 run the resize steps run the scroll steps这些步骤是什么意思呢?不可能每次microtask执行完就resize和scroll吧,所以请问博主你看懂这块了吗?

@creeperyang

This comment has been minimized.

Copy link
Owner Author

commented Mar 5, 2017

@Ma63d 你给的链接里描述很清晰,microtask跟你说的这些无关。

链接描述,一个event loop的典型步骤:

  1. Select the oldest task...
  2. Set the event loop's currently running task to the task selected in the previous step.
  3. Run: Run the selected task.
  4. Set the event loop's currently running task back to null.
  5. Remove the task that was run in the run step above from its task queue.
  6. Microtasks: Perform a microtask checkpoint.
  7. Update the rendering
  8. If this is a worker event loop...
  9. Return to the first step of the event loop.

Update the rendering是event loop的一个步骤,Microtasks也是event loop的一个步骤,在执行完Microtasks,浏览器开始Update the rendering,至于run the resize steps等是Update the rendering里面的内容,跟Microtasks无关。

@Ma63d

This comment has been minimized.

Copy link

commented Mar 5, 2017

@creeperyang 嗯,我当时的表示有误,我问的意思是执行完所有的microtask之后执行的Update the rendering,不是说单独执行一个之后立马执行run the resize steps/run the scroll steps。我当时描述不准确,不好意思把你引入到了莫名的关注点上面去了。

我当时一开始没看懂,为什么Update the rendering竟然去执行resize窗口和scroll窗口。不可能每次task执行完,然后清空完microtask队列之后就让屏幕resize/scroll一次吧。所以评论了一开始的内容。

最近仔细看了一下run the scroll steps不是scoll窗口,每次我们scoll的时候视口或者dom就已经立即scroll了,并把document或者dom加入到 pending scroll event targets中,而run the scroll steps具体做的则是遍历这些target,在target上触发scroll事件。
run the resize steps也是相似的,这个步骤是触发resize事件。

至于后续的media query, run CSS animations and send events等等也是相似的,都是触发事件,第10步和第11步则是执行我们熟悉的requestAnimationFrame回调和IntersectionObserver回调(第十步还是挺关键的)。

第十二步才是对UI执行render,这里应该就是重排、重绘和把更改后的样式真正render改到dom上面去。

@zyg-github

This comment has been minimized.

Copy link

commented Mar 10, 2017

SetTimeout 内的回调属于 macrotask, 会在下一个 Event Loop 中执行

@keenwon

This comment has been minimized.

Copy link

commented Mar 11, 2017

定位到开头的题目,流程如下:

  1. 当前task运行,执行代码。首先setTimeout的callback被添加到tasks queue中;
  2. 实例化promise,输出 1; promise resolved;输出 2;
  3. promise.then的callback被添加到microtasks queue中;
  4. 输出 3;
  5. 已到当前task的end,执行microtasks,输出 5;
  6. 执行下一个task,输出4。

关于第2,3步,不是promise.resolve()的时候,promise.then的callback已经被添加到microtask queue了吗?然后输出2,然后跳到第四步输出3.

@creeperyang

This comment has been minimized.

Copy link
Owner Author

commented Mar 11, 2017

@keenwon

为了让第2,3步更清晰一点,可以写成下面这样。

// 1
setTimeout();
// 2
var promise = new Promise(executor);
// 3
promise.then(callback)
// 4
console.log(3)

其中,得到Promise的实例promise的时候,exectuor作为参数传给Promise的构造函数同步执行。所以输出了数字12

构造函数执行完后,我们得到了promise(它是resolved)。

调用promise.thencallback被添加到microtasks的队列中。

console.log(3)执行完后,当前执行栈为空,则开始执行microtasks。

@keenwon

This comment has been minimized.

Copy link

commented Apr 27, 2017

@mqliutie
你这例子不对吧?你的 setTimeoutnew Promise 内部呀,大兄弟;setTimeout 不执行 then 也永远不执行,这是有明显的先后顺序的

@keenwon

This comment has been minimized.

Copy link

commented Apr 27, 2017

image

image

@mqliutie

This comment has been minimized.

Copy link

commented Apr 28, 2017

@keenwon 通过这个查看调用栈和任务队列

调用栈为什么还没有清空,就执行了任务队列的定时任务?

@jawil

This comment has been minimized.

Copy link

commented May 12, 2017

学习学习

@qinyang912

This comment has been minimized.

Copy link

commented May 31, 2017

应该是每个浏览器至少有一个 event loop 吧

@creeperyang

This comment has been minimized.

Copy link
Owner Author

commented Jun 3, 2017

补充关于 同步/异步/阻塞/非阻塞 的理解。搬迁自已废弃的#15,因为觉得和 event loop 强相关,放到一起便于参照。

Synchronize / Asynchronize / Block / Non-block 一个从分布式系统角度的理解

这一段主要来自 知乎 怎样理解阻塞非阻塞与同步异步的区别? 严肃的答案 ,并参照了 stackoverflow 的相关问题。

同步与异步,阻塞与非阻塞是两组概念,但容易混淆,比如同步不代表阻塞,同步也可以是非阻塞的。

同步与异步

  1. 同步和异步关注的是 消息通信机制 (synchronous communication/ asynchronous communication)

  2. 所谓同步,就是在发出调用时,

    1. 在没有得到结果之前,调用不返回。
    2. 一旦调用返回,即得到返回值。

    换句话说,就是由 调用者主动等待 这个调用的结果。

  3. 异步则是相反,调用在发出之后,调用就直接返回,但没有返回结果。

    换句话说,当一个异步过程调用发出后,调用者不会立刻得到结果。而是在调用发出后,被调用者通过状态、通知来通知调用者,或通过回调函数处理这个调用。

阻塞与非阻塞

  1. 阻塞和非阻塞关注的是 程序在等待调用结果(消息,返回值)时的状态

  2. 阻塞调用是指调用结果返回之前,当前线程会被挂起。调用线程只有在得到结果之后才会返回。

  3. 非阻塞调用指在不能立刻得到结果之前,该调用 不会阻塞当前线程

举例

以你打电话让书店老板查找某本书为例来讲:

  • 同步通信机制:老板说,“你稍等,我查下”,然后查好后(可能会5秒或者1天)告诉你结果(返回结果)。
  • 异步通信机制:老板说,“我查好后打电话你”,然后直接挂电话(无返回结果)。查好后打电话你(“回电”这种方式回调/通知你)。
  • 阻塞式调用:打电话给老板时,你会一直把自己“挂起”,直到结果返回。
  • 非阻塞式调用:打电话给老板时,不管老板有没有告诉你,你自己先一边玩了,但可能会每过几分钟跟老板check一下有没有返回结果。

总结:阻塞与非阻塞 与 是否同步异步无关(跟老板通过什么方式回答你无关)。 所以,也可以说, 同步/异步 针对的是 通信机制(被调用方怎么通知调用方),阻塞/非阻塞 针对的是 调用方在等待结果时的状态。

此外,同步/异步 和 阻塞/非阻塞 可以相互组合 (from 吴昌明 的评论):

  • 同步阻塞:你打电话问老板有没有某书,老板去查,在老板给你结果之前,你一直拿着电话等待老板给你结果,你此时什么也干不了。
  • 同步非阻塞:你打电话过去后,在老板给你结果之前,你拿着电话等待老板给你结果,但是你拿着电话等的时候可以干一些其他事,比如嗑瓜子。
  • 异步阻塞:你打电话过去后,老板去查,你挂掉电话,等待老板给你打电话通知你,这是异步,你挂了电话后还是啥也干不了,只能一直等着老板给你打电话告诉你结果,这是阻塞。
  • 异步非阻塞:你打电话过去后,你就挂了电话,然后你就想干嘛干嘛去。只用时不时去看看老板给你打电话没。

这个回答我觉得是很清晰,易于理解的解释,不过下面还是会列出一些其它角度的解释,方便对照吧。

Asynchronous vs synchronous execution, what does it really mean?

这是关于同步/异步执行的理解,几个高票答案总结下可知:

  1. 同步执行是每个task必须结束后才能继续下个task,前后task是顺序的,有依赖的。
  2. 异步执行是多个task可以并行,相互不依赖。

这也是一个角度,可参照。

@szouc

This comment has been minimized.

Copy link

commented Jun 3, 2017

是否可以这样认为,同步异步是两者的通信方式,CPU 同 I/O 或数据库。阻塞非阻塞是个体的工作方式,进程的单线程、多线程。单线程工作方式的进程就是阻塞的,多线程工作方式的进程就是非阻塞的。

NodeJS 应该是利用了事件循环既实现了两者间的异步通信,又解决了单线程的阻塞难题。(对比时分复用,将单线程按事件划分成虚拟的多线程,但是对CPU密集程序来说它还是阻塞的)

@creeperyang

This comment has been minimized.

Copy link
Owner Author

commented Jun 4, 2017

@szouc 阻塞/非阻塞和单/多线程没有对应关系,同步异步和线程也没有关系。

在Node.js中,JavaScript是单线程的,所以必然不能使用阻塞IO(阻塞就没法实现高并发)。

Node.js uses an event-driven, non-blocking I/O model that makes it lightweight and efficient.

通过libuv,Node.js 在多平台支持了非阻塞IO:

  1. Network IO,依赖Linux上的epoll,OSX和BSD类OS上的kqueue,SunOS上的event ports以及Windows上的IOCP等内核事件通知机制。
  2. File IO,使用thread pool (线程池,多线程)。
@channg

This comment has been minimized.

Copy link

commented Nov 8, 2017

@qingmingsang 所以我可以理解,从微观角度 promise.then是microtask 但是宏观上,那是与setTimeout处于同一个macrotask

@shaodahong

This comment has been minimized.

Copy link

commented Nov 8, 2017

@creeperyang 我理解的是event loop是有两个的队列,task和job,他们都遵循着先进先出,每执行完一个task后会去执行job,job执行完成后再去执行task,以此类推

@shaodahong

This comment has been minimized.

Copy link

commented Nov 8, 2017

@creeperyang 大概的一个伪代码就是

const job = [];
const task = [];

// 一些同步操作
something();

// event loop
job.forEach(v => v());

// job 执行完
task.forEach(v=> {
    v();
    job.forEach(v => v()); //在 loop 期间会有新任务加入也会执行,
})

执行策略的优化就是看哪些属于 job,哪些属于 task,然后根据业务来进行优化吧,尽可能的让视图渲染放到 job中,promise.then 是 job,setTimeout 是 task

@mrhack

This comment has been minimized.

Copy link

commented Dec 8, 2017

<script type="text/javascript">
	setTimeout(function(){
		console.log(1);
	})
	Promise.resolve().then(function(){
		console.log(2);
	})
	console.log(3);
	document.write('<script type="text/javascript">console.log(4)<\/script>');
</script>
<script type="text/javascript">
	document.write('<script type="text/javascript">console.log(5)<\/script>');
	console.log(6)
</script>

结果是什么?

@sponia-joker

This comment has been minimized.

Copy link

commented Feb 4, 2018

请教博主一个问题,烦请指教!当我反复查看html规范时候

An event loop has one or more task queues. A task queue is an ordered list of tasks, which are algorithms that are responsible for such work as:

  1. Events
    Dispatching an Event object at a particular EventTarget object is often done by a dedicated task.
    2.Parsing
    The HTML parser tokenizing one or more bytes, and then processing any resulting tokens, is typically a task.
    ....

规范里面说到event loop有多个task queues.一个task queue就是任务的列表,它们负责以下工作。
看里很久有两个疑惑,恳请解答
1.dispatching an event 不是浏览器有专门的线程监听各种事件吗?然后相应的event handler 会进入task queues.为什么规范里说:是任务队列Dispatching an Event。因为任务队列里的任务都会被JavaScript engines执行,也就是说是JavaScript engines在触发事件?
2.html解析不是通过渲染引擎来解析的吗,怎么这里又说是JavaScript engines解析?在一次event loop中,如果页面需要更新。这时候会停止JavaScript引擎,启动渲染引擎来更新页面吗?
谢谢了
@creeperyang

@googya

This comment has been minimized.

Copy link

commented Feb 22, 2018

new Promise(resolve => {
    resolve(1);
    console.log(4)
    // new Promise(r => r(2)).then(e => console.log(e))
    Promise.resolve(2).then(e => console.log(e))
    Promise.resolve(5).then(e => console.log(e))
}).then(v => console.log(v))

console.log(3)

在 Promise 中, 如果还有 promise, 那么先得把内部的 promise 顺序执行完,然后执行外面的 promise? 我是根据得到的结果推理出来的, 暂时没看到对应的文档说明, issue 主怎么看?

@creeperyang

This comment has been minimized.

Copy link
Owner Author

commented Feb 22, 2018

@googya

// 执行第 1 步
const p = new Promise(resolve => {
    resolve(1);
    // 第 1.1 步,首先输出 4 
    console.log(4)
    // 第 1.2 步,压入microtask 队列
    Promise.resolve(2).then(e => console.log(e))
    // 第 1.3 步,压入microtask 队列
    Promise.resolve(5).then(e => console.log(e))
})

// 执行第 2 步,压入microtask 队列
p.then(v => console.log(v))

// 执行第 3 步
console.log(3)

所以输出结果:

4  --> 3,然后microtask队列支持输出 2 --> 5 --> 1
@googya

This comment has been minimized.

Copy link

commented Feb 22, 2018

@creeperyang 步骤分解之后是更清楚一点。 我之前的疑问是, 为什么 1.2, 1.3 先于步骤2,只是因为 1.2 1.3 在构造器中, 先执行的吧

@creeperyang

This comment has been minimized.

Copy link
Owner Author

commented Apr 12, 2018

标题:从浏览器多进程到JS单线程,JS运行机制最全面的一次梳理
作者:Lichun Dai

一篇从浏览器进程/线程角度来解释浏览器环境中JS运行机制的文章,通俗易懂,对理解浏览器环境的 event loop 很有帮助。

核心知识点:

  1. 现代浏览器(如Chrome)每个tab页对应一个独立进程,有独立的渲染引擎(浏览器内核)实例,内核一般包括以下线程:

    +-------------------------------+
    |+-----------------------------+|
    ||         GUI渲染线程          ||
    ||                             ||
    |+-----------------------------+|
    |                               |
    |+-----------------------------+|
    ||         JS引擎线程           ||
    ||                             ||
    |+-----------------------------+|
    |                               |
    |+-----------------------------+|
    ||         事件触发线程          ||
    ||                             ||
    |+-----------------------------+|
    |                               |
    |+-----------------------------+|
    ||        定时器触发线程         ||
    ||                             ||
    |+-----------------------------+|
    |                               |
    |+-----------------------------+|
    ||       异步http请求线程        ||
    ||                             ||
    |+-----------------------------+|
    |                               |
    +-------------------------------+
    
  2. JS 线程用于解析和执行 JavaScript。图中显见:一个渲染引擎进程只有一个 JS 线程——JS 是单线程的。那么JS的异步能力是谁赋予的?JS引擎本身只能不断地去检索任务队列,取出开头的任务去执行(即所谓的event loop),那么 setTimeout/事件回调/ajax请求回调 等异步能力仅仅靠 JS 线程本身是做不到的,上面的图正好可以让初学者们恍然大悟:

    setTimeout 等本质上是 JS 线程调用了其它线程,其它线程在条件达成时把任务塞入队列。

  3. UI 线程和 JS 线程是互相阻塞的,所以不要让 JS 线程长时间运行,从而让 UI 线程被长时间挂起,使 UI 渲染掉帧甚至不响应。

@peze

This comment has been minimized.

Copy link

commented May 19, 2018

我最近正好看到这个地方,颇有疑惑之处,首先,node的makecallback 有调用 env()->tick_callback_function()->Call;所以我刚开始感觉nextTick是在事件循环的回调中调用。但是实际上比如用setTimeout(fn,0)这个是标准的libuv timer然后回调执行fn,那应该是在执行完fn以后再执行nexttick,但实际不是,所以我比较同意是Microtasks来执行的,而且应该不是在同一个线程上,因为node启动platform的时候用了4个线程做线程池,然后再node::start里uv_run前面有个PumpMessageLoop更加重了我的猜测,就是这个 是microtasks来做的nexttick。而且如果我通过执行自己的embed函数 在函数中直接运行uv_run,不通过node::start里面的uv_run执行,nextTick是不会被执行的。

@xianshenglu

This comment has been minimized.

Copy link

commented May 26, 2018

@nicewahson

This comment has been minimized.

Copy link

commented Aug 31, 2018

楼主能说下 tick和loop是啥区别吗?

@wroy1005

This comment has been minimized.

Copy link

commented Oct 24, 2018

<script type="text/javascript">
	setTimeout(function(){
		console.log(1);
	})
	Promise.resolve().then(function(){
		console.log(2);
	})
	console.log(3);
	document.write('<script type="text/javascript">console.log(4)<\/script>');
</script>
<script type="text/javascript">
	document.write('<script type="text/javascript">console.log(5)<\/script>');
	console.log(6)
</script>

结果是什么?

342561

@AdvancedCat

This comment has been minimized.

Copy link

commented Jun 12, 2019

楼主好,根据最近一次的新闻,WHATWG全权接管了HTML和DOM的标准制定权(链接)。
您在博文中HTML5标准的引用就有点过时了。而且根据最新标准制定Task Queue不是一个队列,它是一个任务集合(A task queue is a set of tasks)。
与时俱进一下。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.