1+1=10

扬长避短 vs 取长补短

JavaScript学习笔记(五)

接前面的:

继续学习JavaScript。

Event Loop ?

概念似乎不复杂,但是,术语很乱!

ECMA-262 标准中既没有Event Loop,也没有MicroTask、MacroTask。ECMA-262 中只提到了 Job 的概念,没提及任何Task术语。

JavaScript在异步的进化和支持上,是不是可以分成如下阶段?

阶段 ECMAScript JavaScript
单线程执行模型 ?
引入事件循环 -
回调函数模型 -
Promises的引入
Async/Await的出现
  • ECMAScript 没有事件循环,但是JavaScript在WEB 中一直在使用。
  • ECMAScript 引入了Promises,与事件循环没什么关系。

JavaScript Web运行时与Event loop

尽管 ECMA-262 中也没有出现 Event Loop 这个名字。但是在MDN中的WEB API部分,whatwg中的HTML DOM标准部分,以及Nodejs中都有 event loop的身影。所以需要从特定的运行时看起...

概念

JavaScript Web运行时的几个概念:

  • Stack:函数调用形成frame的stack
  • Heap:对象分配在Heap中,指代内容中一大块区域。
  • Queue:包含待处理的message的列表,每一个message有一个关联的处理函数

javascript_runtime_environment_example

事件循环

事件循环的概念简单,和Qt一样:

在事件循环的某个时刻,运行时开始处理事件循环中的消息,消息被从队列中移除,并调用与之对应的函数,将消息作为输入参数传递给该函数。调用函数会为该函数的使用创建一个新的堆栈帧。函数的处理会持续进行,直到堆栈再次为空为止。然后,事件循环将处理队列中的下一消息(如果有的话)。

但是,术语上似乎有些精神分裂。仅从MDN的资料看,也很乱:它一边叫事件循环,一边又叫消息队列,有时又称呼任务队列。Event、Message、Task,到底用哪一个?

比如说,setTimeout() 到底是往队列中添加了一个message,还是一个task??

MDN的Microtask guide中说:

A task is any JavaScript scheduled to be run by the standard mechanisms such as initially starting to execute a program, an event triggering a callback, and so forth. Other than by using events, you can enqueue a task by using setTimeout()or setInterval().

MDN的Event loop中说:

The first two arguments to the function setTimeout() are a message to add to the queue and a time value (optional; defaults to 0).

Event

Event 是一个术语,也是JavaScript中的一种对象。

Web API中大量使用Event。

  • Event 可以通过用户动作触发。比如,键盘、鼠标、或者其他API
  • Event 可以通过程序触发,比如调用元素的方法 HTMLElement.click()
  • 也可以定义Event,而后通过EventTarget.dispatchEvent()发送到指定目标

Event需要和处理函数绑定,可以用:

  • EventTarget 的 addEventListener()。 Element等都是EventTarget派生类
  • event handler属性,比如onclick、onchange、onclose、...

两者之间,前者有多种优势:1)可以添加多个处理函数;2)在listener被激活时有更精细控制,比如capturing 与 bubbling传播方式;3)适用于任何event target。

SetTimeout

setTimeout()用于创建一个计划在一段时间后执行的任务,这个任务在计时器到期时会被添加到消息队列中,然后通过事件循环被处理。

setTimeout() 是 Web API 的一部分,涉及到事件循环(Event Loop)的概念,但 setTimeout 本身并不直接创建一个Event对象。

Job、Task、MicroTask、MacroTask

这个概念让人晕乎:

  • ECMA-262 中只提到了 Job 的概念,没提及任何Task术语。

  • MDN 中提到 Task 和 MicroTask。

  • 社区中 MacroTask 和 MicroTask 用的很普遍,据说最早是V8先这么叫的?

是不是可以这么认为?

  • ECMAScript 没有事件循环,但是JavaScript在WEB 中一直在使用。和前面事件循环相关的是 Task(或者叫MacroTask)。
  • ECMAScript 引入了Promises。相关内容,为了区分事件循环中的Task,称为 MicroTask

Microtask

MDN如此介绍Microtask:

A microtask is a short function which is executed after the function or program which created it exits and only if the JavaScript execution stack is empty, but before returning control to the event loop being used by the user agent to drive the script's execution environment.

Microtask是一个函数,它在创建它的函数或程序结束且stack为空时被执行,它在控制转交给事件循环之前被执行。换言之,这个东西和事件循环没有关系,尽管它有一个队列。

Microtask: * Promise 回调函数(.then,.catch,.finally) * MutationObserver 接口【web api】 * process.nextTick() 【Node js】 * queueMicrotask() 函数 【web api】 * async函数中的 await 表达式

Promise

不同于Event Loop,Promise是ECMAScript中正经存在的东西。

初次接触Qt中的QJSEngine时,发现这东西满足ECMA-262标准,支持Promise,但是并没有任何EventLoop的身影,感觉还是挺神奇。

参考

  • https://developer.mozilla.org/en-US/docs/Web/JavaScript/Event_loop
  • https://developer.mozilla.org/en-US/docs/Web/API/HTML_DOM_API/Microtask_guide/In_depth
  • https://developer.mozilla.org/en-US/docs/Web/API/Event
  • https://developer.mozilla.org/en-US/docs/Web/API/HTML_DOM_API/Microtask_guide
  • https://developer.mozilla.org/en-US/docs/Web/API/MutationObserver/MutationObserver
  • https://html.spec.whatwg.org/multipage/webappapis.html#event-loops
  • https://tc39.es/ecma262/2023/#job

Comments