Javascript的一些基础概念

JavaScript执行引擎在宿主环境中是单线程的,这意味着在同一时间内只能执行一个任务。在Javascript运行期间,引擎会创建和维护相应的堆(heap)和栈(stack)这两个数据结构;堆是存放数据变量的地方(这里很多前端er有个误区,认为js的引用类型存放在堆中,基础类型的变量是存放在栈中的),栈就是指执行栈。

对于浏览器来说,当它第一次加载你的<script>标签时,它默认进入了全局执行环境;如果你在全局代码中调用了一个函数,执行流就会进入到这个函数当中,创建一个新的执行环境并且把这个环境添加到执行栈的顶部。如果你在当前的函数中又调用了其他函数,同样的步骤会再来一次。浏览器始终执行当前在栈顶部的执行环境。一旦函数完成了当前的执行环境,它就会被弹出栈的顶部, 把控制权返回给栈中的下个执行环境:

上述任务都是同步执行的,这就会导致一个很常见的问题:如果执行了一段非常耗时的同步代码 例如请求数据、上传文件等,浏览器就会长时间无法渲染,页面阻塞不能响应交互。对于需要处理这类IO密集型任务的浏览器来说,Javascript当然是需要异步非阻塞机制的,这就是下面要讲的事件循环机制。

Event Loop

上面讲了执行栈中的所有任务从顶向下同步执行;但当遇到一些需要异步执行的任务,如ajaxsetTimeout等时,会立即返回函数,然后将异步操作交给浏览器内核中的其他模块处理(如timer、network、DOM Binding模块等),接着主线程继续往下执行栈中的任务。当这些异步操作完成后如ajax接受完响应、setTimeout到达指定延时;这些任务的回调就会被放入到任务队列中。不同的异步任务的回调函数会被放入不同的任务队列之中。

在浏览器中是有两种不同类型的异步任务,国内的文章好像都是叫做“宏任务”和“微任务”,但在HTML标准(没错,事件循环这玩意是在HTML标准里的而不是ECMAScript标准定的)中并没有这么区分。在标准中,我们常说的“宏任务”就是指Task,常见的来源(Task Source)有:

  • DOM操作
  • UI交互
  • 网络请求
  • History APIs
  • 定时器任务
  • 。。。。。。

不同Task Source所产生的Task还会有自己的队列(Task Queue);当多个Task Queue都存在Task时,浏览器会自行调度决定先执行哪个;但同一Task Queue里的Task一定是按先进先出(FIFO)的顺序执行的。

后面出的新特性如Promise和MutationObserver,通常就是标准中的'Microtask'。与Task Queue不同的是,Microtask Queue只有一个。

当执行栈中所有任务都执行完了后,就会去Event Loop中去取出任务,放到执行栈中同步执行。Event Loop做了哪些事情呢,参考标准(Processing model),大概总结如下:

  1. 从Task Queue取出一个Task执行;至于如何选择哪个Task Queue(前面说过,Event Loop中会有多个Task Queue)取决于浏览器实现方。

  2. 依次执行Microtask Queue中的所有Microtask。注意,在本轮循环中新增的Microtask也会在此执行完

  3. 设置变量now的值。后续如果执行window.requestAnimationFrameIntersection Observer的话,传入回调函数中的时间戳就是这个变量now

  4. 更新渲染

    • 浏览器会根据刷新频率和页面状态等因素来决定是否要跳过该次渲染更新。例如 当浏览器试图达到60Hz的刷新频率时,会让更新渲染的次数在一秒内最多达到60次(16.6ms一次),但如果浏览器发现页面无法稳定维持该帧率的话,就会降到30Hz,那么更新渲染的几率就被降低了一半;或者当前页面的可见性为否时,浏览器可以将该页面降低至每秒4次甚至更低的更新渲染次数;又或者当前的渲染不会有可见的差异等。

    • 如果确认需要更新渲染:

      1. 触发resizescrollfullscreen等事件的处理函数并传入刚刚前面设置的now作为时间戳;并不是说到这里才会更新视图,窗口大小和滚动是会马上更新的,只是需要在一次事件循环中走到这一步时,才会触发这些事件的分发

      2. 执行帧动画回调,传入刚刚前面设置的now作为时间戳;window.requestAnimationFrame就是在此时执行的

      3. 执行Intersection Observer API的回调

      4. 渲染页面内容并提交

  5. 每当一轮循环结束后,会判断Task Queue和Microtask Queue是否为空。都为空的话则再根据空闲周期算法决定是否执行requestIdleCallback回调。

依据HTML标准再探Javascript事件循环及其与浏览器渲染的关系的更多相关文章

  1. c#封装DBHelper类 c# 图片加水印 (摘)C#生成随机数的三种方法 使用LINQ、Lambda 表达式 、委托快速比较两个集合,找出需要新增、修改、删除的对象 c# 制作正方形图片 JavaScript 事件循环及异步原理(完全指北)

    c#封装DBHelper类   public enum EffentNextType { /// <summary> /// 对其他语句无任何影响 /// </summary> ...

  2. 一篇文章图文并茂地带你轻松学完 JavaScript 事件循环机制(event loop)

    JavaScript 事件循环机制 (event loop) 本篇文章已经默认你有了基础的 ES6 和 javascript语法 知识. 本篇文章比较细致,如果已经对同步异步,单线程等概念比较熟悉的读 ...

  3. JavaScript 事件循环

    JavaScript 事件循环 事件循环 任务队列 async/await 又是如何处理的呢 ? 定时器问题 阻塞还是非阻塞 实际应用案例 拆分 CPU 过载任务 进度指示 在事件之后做一些事情 事件 ...

  4. Javascript:再论Javascript的单线程机制 之 DOM渲染时机

    Javascript:再论Javascript的单线程机制 之 DOM渲染时机 背景 Javascript是单线程事件驱动的,所有能看到的Javascript代码都是在一个线程执行,定时器回调和AJA ...

  5. 学习笔记---Javascript事件Event、IE浏览器下的拖拽效果

    学习笔记---Javascript事件Event.IE浏览器下的拖拽效果     1. 关于event常用属性有returnValue(是否允许事件处理继续进行, false为停止继续操作).srcE ...

  6. 深入理解 JavaScript 事件循环(二)— task and microtask

    引言 microtask 这一名词是 JS 中比较新的概念,几乎所有人都是在学习 ES6 的 Promise 时才接触这一新概念,我也不例外.当我刚开始学习 Promise 的时候,对其中回调函数的执 ...

  7. [译] 深入理解 JavaScript 事件循环(二)— task and microtask

    引言 microtask 这一名词是 JS 中比较新的概念,几乎所有人都是在学习 ES6 的 Promise 时才接触这一新概念,我也不例外.当我刚开始学习 Promise 的时候,对其中回调函数的执 ...

  8. JavaScript 事件循环及异步原理(完全指北)

    引言 最近面试被问到,JS 既然是单线程的,为什么可以执行异步操作? 当时脑子蒙了,思维一直被困在 单线程 这个问题上,一直在思考单线程为什么可以额外运行任务,其实在我很早以前写的博客里面有写相关的内 ...

  9. 深入浅出Javascript事件循环机制

    一.JS单线程.异步.同步概念 众所周知,JS是单线程(如果一个线程删DOM,一个线程增DOM,浏览器傻逼了-所以只能单着了),虽然有webworker酱紫的多线程出现,但也是在主线程的控制下.web ...

  10. 深入理解 JavaScript 事件循环(一)— event loop

    引言 相信所有学过 JavaScript 都知道它是一门单线程的语言,这也就意味着 JS 无法进行多线程编程,但是 JS 当中却有着无处不在的异步概念 .在初期许多人会把异步理解成类似多线程的编程模式 ...

随机推荐

  1. 云小课|创建DDS只读节点,轻松应对业务高峰

    阅识风云是华为云信息大咖,擅长将复杂信息多元化呈现,其出品的一张图(云图说).深入浅出的博文(云小课)或短视频(云视厅)总有一款能让您快速上手华为云.更多精彩内容请单击此处. 摘要:为了扩展主节点的读 ...

  2. 混合编程:如何用pybind11调用C++

    摘要:在实际开发过程中,免不了涉及到混合编程,比如,对于python这种脚本语言,性能还是有限的,在一些对性能要求高的情景下面,还是需要使用c/c++来完成. 本文分享自华为云社区<混合编程:如 ...

  3. 记一次 .NET某MES自动化桌面程序 卡死分析

    一:背景 1. 讲故事 前些天有位朋友在微信上找到我,说他们的客户端程序卡死了,让我帮忙看下是什么原因导致的?dump也拿到了手,既然有了dump就开始正式分析吧. 二:WinDbg 分析 1. 什么 ...

  4. java中类的普通初始化块一定在静态初始化块后运行吗

    大部分教程都会告诉我们静态初始化块和静态字段总是在初始化块和普通类字段前运行,事实上也确实如此,直到我看到下面这样的代码: public class Test { static Test test = ...

  5. Python使用pandas库读取csv文件,并分组统计的一个例子

    代码: # coding=gbk # 从HostWrites.csv读取数据并分组统计 import pandas import datetime print "\r\n从 HostWrit ...

  6. Python | BitMap算法及其实现

    BitMap概述 本文介绍 BitMap 算法的应用背景,算法思想和相关实现细节. 概括而言,BitMap 主要用来解决海量数据中元素查询,去重.以及排序等问题.这里对海量数据场景的强调,似乎暗示了这 ...

  7. 1 分钟在 Serverless 上部署现代化 Deno Web 应用

    作者 | 连喆人(掌上乾坤公司) 本文选自 "Serverless 函数计算征集令" 征文 利用 Serverless 的水平扩展与按量付费优势, 结合自定义运行时, 实现 Web ...

  8. vue 状态管理 三、Mutations和Getters用法

    系列导航 vue 状态管理 一.状态管理概念和基本结构 vue 状态管理 二.状态管理的基本使用 vue 状态管理 三.Mutations和Getters用法 vue 状态管理 四.Action用法 ...

  9. WebGPU光追引擎基础课:课程介绍

    大家好~我开设了"WebGPU光追引擎基础课"的线上课程,从0开始,在课上带领大家现场写代码,使用WebGPU开发基础的光线追踪引擎 课程重点在于基于GPU并行计算,实现BVH构建 ...

  10. UNI-APP 使用Echart

    UNI-APP 使用Echart(含地图教程) https://blog.csdn.net/weixin_43548442/article/details/121468189 uniapp使用echa ...