JavaScript进阶之执行机制

不太正确的总结:

ES5

ES5事件轮询较为简单。

  1. 主线程执行栈在初次页面后首先会渲染页面;
  2. 所有同步任务都在主线程上执行,形成一个执行栈(execution context stack);
  3. 异步任务有了运行结果,会通过在"事件队列"之中注册一个事件;
  4. 一旦"执行栈"中的所有同步任务执行完毕,系统就会读取"任务队列"。那些对应的异步任务,于是结束等待状态,进入执行栈,开始执行(setTimeOut事件还会检查定时器);
  5. 主线程不停重复以上三个步骤。

ES6

ES6中事件轮询有一些细微的差别(多了promise之后)。

注意点:

  • 宏任务按顺序执行,且浏览器在每个宏任务之间渲染页面
  • 所有微任务也按顺序执行,且在以下场景会立即执行所有微任务
    • 每个回调之后且js执行栈中为空。
    • 每个宏任务结束后。

宏任务和微任务细节可以看这篇文章 Tasks, microtasks, queues and schedules,或者中文译文JS事件循环机制(event loop)之宏任务、微任务。英文原文中有执行动画,可以配合译文阅读。

以上总结不是很严谨。我查了一些资料想要弄清楚“宏任务”和“微任务”的概念是什么时候出现的,然而并没有找到。但是查找过程中发现了一些有趣的事情。

  1. 事件轮询(Event Loop)是Html标准,而不是JavaScript标准;
  2. 关于事件轮询详细标准是在Html5中出现的;

event loop 的详细处理模型就在html标准中,看完就明白了。https://www.w3.org/TR/2018/WD-html53-20181018/webappapis.html#event-loops。这里做一个简单翻译。

译文:

定义

为了协调事件,用户交互,脚本,渲染,网络等,用户代理(user agents,可以理解为一个浏览器客户端)必须使用该节定义的事件循环(event loops)。总共有两种类型的event loops:浏览器上下文(browser context,理解为一个页面,如标签,窗口,包括frames,iframe)和workers。

一个用户代理至少有一个浏览器上下文,一个浏览器上下文必须包含一个event loop。浏览器上下文结束,那么event loop也跟着结束。

一个event loop有一个或多个任务队列( task queues),任务队列是一系列排好序的任务组成,这些任务有:

  • Events(事件),常见的事件
  • Pasring(解析),html解析
  • Callbacks(回调),回调函数
  • Using a resource(使用某个资源),以非阻塞方式获取资源(页面加载图片等)
  • Reacting to DOM manipulation(响应DOM操作),响应点击等DOM操作

当用户代理向任务队列添加一个任务时,同一个任务源的任务必须被添加进同一个任务队列中。比如一个用户代理有两个任务队列,第一个任务队列来处理鼠标和键盘事件(user interaction task source),第二个任务队列来处理其他事件,那么所有鼠标和键盘产生的事件就必须添加到第一个任务队列中。

每一个任务队列都有一个当前运行任务(currently running task),用来处理重入(reentrancy,函数重复调用),初始化为null。每个事件循环还有一个防止微任务重复调用的标志(performing a microtask checkpoint flag,正在执行微任务标识),初始化为false。

处理模型(Processing model,重点)

一个事件循环存在时必须不停地执行以下步骤:

  1. 从event loop的一个事件队列中取出最先进入(原文oldest)的任务。如果有任务,则忽略以下操作。某些情况下,document还没有完全激活,那么用户代理可能会选择一个空的任务队列,如果是这样,跳转步骤6。
  2. 将event loop的当前运行任务设置为上步骤选中的任务
  3. Run:执行选中的任务。
  4. 当前运行任务设为null。
  5. 将该任务从任务队列中移除。
  6. Microtasks:执行微任务。详细见下方。
  7. 更新渲染。如果这个event loop是一个浏览器上下文event loop,那么执行以下子步骤:
    1. 更新Performance对象的now()方法的返回值为现在(now)
    2. 更新和当前事件循环相关Document对象列表,一般不需要排序,除了以下两种情况:

      • Document B嵌套于Document A,那么列表中B必须在A后面。
      • 如果A和B同时嵌套于C,那么A和B在列表中顺序必须满足他们在C中树顺序(tree order)。

      以下的步骤必须根据列表中Document的顺序重复执行 

    3. 如果一个顶层的浏览器上下文B在更新渲染中没有变化,那么会将他从document列表中移除。(操作没有影响到他,就移除他,防止渲染性能影响,比如在iframe中操作,不会触发父页面的重新渲染)
    4. 如果一个嵌套的浏览器上下文B在更新渲染中没有变化,那么会将他从document列表中移除
    5. 对于列表中每一个完全激活的页面,执行resize(执行resize相关事件)步骤,更新now时间
    6. 对于列表中每一个完全激活的页面,执行scroll(执行scroll相关事件)步骤,更新now时间
    7. 对于列表中每一个完全激活的页面,执行媒体查询和修改(执行media query和onchange,change相关事件)步骤,更新now时间
    8. 对于列表中每一个完全激活的页面,执行css动画并发送事件步骤,更新now时间
    9. 对于列表中每一个完全激活的页面,执行全屏渲染步骤,更新now时间
    10. 对于列表中每一个完全激活的页面,执行动画框架回调步骤,更新now时间
    11. 对于列表中每一个完全激活的页面,更新渲染或者用户交互
  8. 如果这是一个worker 事件循环,在事件循环中的任务队列没有任务而且WorkerGlobalScope 的关闭标识为true,那么销毁改事件循环,终止所有步骤,恢复到运行一个worker的步骤。
  9. 返回事件循环的第一步。

每个事件循环都有一个微任务队列(microtask queue),一个微任务是一个一开始就放在微任务队列的任务(不在任务队列中)。总共有两种微任务:单独的回调微任务(solitary callback microtasks)复合微任务(compound microtasks)。

Microtasks处理(perform a microtask checkpoint):

  1. 正在执行微任务标识performing a microtask checkpoint flag)设为true
  2. 微任务队列处理:如果微任务队列为空,跳转到 完成 节点
  3. 选择 微任务队列最先进入(原文oldest)的微任务
  4. 事件循环的当前执行任务设置为上一步选择的微任务
  5. 执行:执行该微任务
  6. 这可能会涉及到脚本回调,可能会再次执行微任务处理(perform a microtask checkpoint),所以使用“正在执行微任务标识“来避免函数重入。

  7. 当前执行任务设为null
  8. 从微任务队列中移除该任务,返回到微任务队列处理步骤
  9. 完成:通知每一个受此事件循环的影响环境对象(environment settings object)他们的rejected promises 
  10. 清除索引数据库事务
  11. 正在执行微任务标识设为false

任务源

  • Dom操作任务源
  • 用户交互任务源
  • 网络任务源
  • 历史返回操作任务源(调用history.back() 等类似api)

总结

  1. 整体运行过程和其他文章没有太大差别,不过没有宏任务队列的概念,只有任务队列和微任务队列概念。
  2. 每执行完任务队列中一个任务,或者任务队列为空时,会立即执行微任务队列中所有任务。
  3. 英文真差(还不如机翻)。。。

原文地址:https://www.cnblogs.com/shaunyang/p/10342240.html

时间: 2024-10-14 04:33:01

JavaScript进阶之执行机制的相关文章

十分钟理解JavaScript引擎的执行机制

关注专栏写文章 十分钟理解JavaScript引擎的执行机制 方伟景 千锋前端开发推动市场提升的学习研究者. 4 人赞同了该文章 首先,请牢记2点: JS是单线程语言 JS的Event Loop是JS的执行机制.深入了解JS的执行,就等于深入了解JS里的event loop 1.灵魂三问:JS为什么是单线程的?为什么需要异步?单线程又是如何实现异步的呢? 技术的出现,都跟现实世界里的应用场景密切相关的.同样的,我们就结合现实场景,来回答这三个问题. (1) JS为什么是单线程的? JS最初被设计

JavaScript定时器与执行机制解析

从JS执行机制说起 浏览器(或者说JS引擎)执行JS的机制是基于事件循环. 由于JS是单线程,所以同一时间只能执行一个任务,其他任务就得排队,后续任务必须等到前一个任务结束才能开始执行. 为了避免因为某些长时间任务造成的无意义等待,JS引入了异步的概念,用另一个线程来管理异步任务. 同步任务直接在主线程队列中顺序执行,而异步任务会进入另一个任务队列,不会阻塞主线程.等到主线程队列空了(执行完了)的时候,就会去异步队列查询是否有可执行的异步任务了(异步任务通常进入异步队列之后还要等一些条件才能执行

JavaScript进阶之执行上下文和执行栈

js引擎的执行过程 执行上下文和执行栈属于js引擎的执行过程的预编译阶段. 执行上下文(Execution Context) 执行上下文是当前 JavaScript 代码被解析和执行时所在环境的抽象概念.可以理解为当执行代码时做的准备工作. 执行上下文按照运行环境被分成三类: 全局执行上下文(JS代码加载完毕后,进入代码预编译即进入全局环境) 函数环境执行上下文(函数调用执行时,进入该函数环境,不同的函数则函数环境不同) eval执行上下文(不建议使用,会有安全,性能等问题) 对于每个执行上下文

理解JavaScript的执行机制

一直没有深入了解过JavaScript的事件执行机制,直到看到了这篇文章:<这一次,彻底弄懂JavaScript执行机制> 才发觉熟悉JavaScript的执行机制非常重要. 毕竟在跟进项目中偶尔需要排查为什么会出现函数执行顺序不一样的情况. 感谢作者浅显易懂的文字让我获益匪浅,以下是自己对JavaScript执行机制的理解,全是流水账. 文章主要叙述: 1:单线程和异步任务 2: 异步任务的分类 3:setTimeout 和 setInterval 的执行方式 单线程和异步任务 JavaSc

彻底弄懂 JavaScript 执行机制

本文的目的就是要保证你彻底弄懂javascript的执行机制,如果读完本文还不懂,可以揍我. 不论你是javascript新手还是老鸟,不论是面试求职,还是日常开发工作,我们经常会遇到这样的情况:给定的几行代码,我们需要知道其输出内容和顺序.因为javascript是一门单线程语言,所以我们可以得出结论: javascript是按照语句出现的顺序执行的 看到这里读者要打人了:我难道不知道js是一行一行执行的?还用你说?稍安勿躁,正因为js是一行一行执行的,所以我们以为js都是这样的: let a

这一次,彻底弄懂 JavaScript 执行机制

本文的目的就是要保证你彻底弄懂javascript的执行机制,如果读完本文还不懂,可以揍我. 文章转自:https://juejin.im/post/59e85eebf265da430d571f89 不论你是javascript新手还是老鸟,不论是面试求职,还是日常开发工作,我们经常会遇到这样的情况:给定的几行代码,我们需要知道其输出内容和顺序.因为javascript是一门单线程语言,所以我们可以得出结论: javascript是按照语句出现的顺序执行的 看到这里读者要打人了:我难道不知道js

JavaScript 执行机制

一.宏任务与微任务 macro-task(宏任务):包括整体代码script,setTimeout,setInterval micro-task(微任务):Promise,process.nextTick 二.JavaScript 执行机制 本文的目的就是要保证你彻底弄懂javascript的执行机制,如果读完本文还不懂,可以揍我. 不论你是javascript新手还是老鸟,不论是面试求职,还是日常开发工作,我们经常会遇到这样的情况:给定的几行代码,我们需要知道其输出内容和顺序.因为javasc

【JS】JavaScript引擎的内部执行机制

 近期在复习JavaScript,看到setTimeout函数时.想起曾经刚学时,在一本书上看过setTimeout()里的回调函数执行的间隔时间有昌不是后面设置的值.曾经没想太多.网上看了JS大神的解释,整理记录下JavaScript引擎的内部执行机制. 首先看一段小程序: <script> alert('第1'); setTimeout(function(){alert('第2');}, 2000); alert('第3'); </script> 输出顺序是:第1.第3,第

JavaScript进阶系列05,事件的执行时机, 使用addEventListener为元素同时注册多个事件,事件参数

本篇体验JavaScript事件的基本面,包括: ■ 事件必须在页面元素加载之后起效■ 点击事件的一个简单例子■ 为元素注册多个点击事件■ 获取事件参数 □ 事件必须在页面元素加载之后起效 有这样一段简单的代码: <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/> <title></title> <style>