JS 异步 ( 一、异步概念、Web worker 基本使用 )


文章目录

  • 异步
    • 代码异步执行概念
    • ES6 之前的异步
  • Web worker

异步

代码异步执行概念

通常代码是自上而下同步执行的,既后面的代码必须等待前面的代码执行完才会执行,而异步执行则是将主线程中的某段代码交由子线程去执行,当交给子线程后,主线程就会继续执行后面代码,而不用等待子线程执行完成,异步是程序语言并行执行的一种手段,通常将耗时的任务交由子线程同时处理,从而提升整体任务耗时。

不严谨的对比一下单线程同步和多线程异步的效率提升(不考虑 CPU 核数和时间片切换问题):

请添加图片描述

ES6 之前的异步


在 ES6 的 Web worker 出现之前,Javascript 确实也可以异步开发,但是要知道的是,那时的 Javascript 是单线程的,之所以能够使用多线程实现异步,其实是依靠 <浏览器内核结构> 的多线程,而不是 JS 本身具备多线程特性。

1. 浏览器内核结构

请添加图片描述

浏览器是多个进程共同配合工作的,所谓浏览器内核指的就是其中的渲染进程,进程主要结构如上图,
渲染进程中又有如下几个重要的线程(这些线程并不是JS的,而是浏览器渲染进程的):

线程功能
GUI 渲染线程 (渲染引擎)解析 HTML 和 CSS,从而构建 DOM
JS 执行线程 (JS 引擎)负责执行 Javascript 代码,内含 <任务队列> 和 <事件循环> 两个重要模块
事件触发线程 (DOM 监听)当事件发生后,将事件的回调函数添加到 JS 引擎的 <任务队列>
定时器线程 (Timer 监听)setTimeout、setInterval 等的计时,达到时间后,将计时器的回调函数添加到 JS 引擎的 <任务队列>
XmlHttpRequest 线程 (AJAX)XmlHttpRequest 的监听,当 XmlHttpRequest 对象状态变化时,将 Ajax 回调函数添加到 JS 引擎的 <任务队列>

GUI 线程和 JS 执行线程不能同时执行,遇见 Javascript 代码时,JS 执行引擎运行优先级更高

2. JS 执行线程的运行机制及与其他线程的搭配

请添加图片描述

(1) 主线程按顺序执行代码,当碰见 setTimeoutsetIntervalXmlHttpRequestDOM 事件 等 Api 时,会将其交给
对应的定时器线程、XmlHttpRequest 线程、事件线程处理,然后主线程继续执行后面的代码

(2) 定时器线程、XmlHttpRequest 线程、事件线程的任务触发后,会将回调函数添加至 JS 线程的任务队列中

(3) 主线程内的任务全部执行完成后,会调用事件循环器拉取任务队列中的任务到主线程,至此一个事件循环周期结束

3. 分析 setTimeout 等计时不准的问题

<script>(function async(){console.log('主线程执行1')setTimeout(function(){console.log('setTimeout 计时结束')}, 3000)console.log('主线程执行2')while(true){}}())// 输出:// 主线程执行1// 主线程执行2
</script>

预想结果是 3 秒后输出 <setTimeout 计时结束>,但在实际结果中,setTimeout 即使达到时间也没有执行,现在明
白了 <JS 执行线程的运行机制及与其他线程的搭配> 的原理,就可以解释这个现象了,因为主线程中有 while(true)
而主线程执行不完,就不会执行事件循环器拉取任务队列中 setTimeout 的回调函数,所以 setTimeout 的回调一直没有被调用


4. 微观队列

在 ES6 之后,<JS 执行线程的运行机制> 的整体流程有一点变化,事件循环器除了调用 <任务队列> 以外, 又多了
一个队列,为了区分,原队列改称为 <宏队列>,新队列称为 <微队列> ,<微队列> 中比较典型的就是状态为 fulfilled
或 rejected 的 Promise 对象的处理函数。在一次事件循环中优先执行 <微队列> 中的任务。

请添加图片描述

举例 - 事件循环器优先执行 <微队列> 中的任务:

<script>console.log("主线程任务1")// 定时器setTimeout(()=>{console.log("延迟零毫秒的定时任务1")},0)// 状态为 fulfilled 的 PromisePromise.resolve().then(()=>{console.log("状态为 fulfilled 的 Promise")})// 定时器setTimeout(()=>{console.log("延迟零毫秒的定时任务2")},0)console.log("主线程任务2")// 输出:// 主线程任务1// 主线程任务2// 状态为 fulfilled 的 Promise// 延迟零毫秒的定时任务1// 延迟零毫秒的定时任务2
</script>

复杂一点的举例(事件循环每次只获取一个任务):

<script>console.log("主线程任务1")// 定时器setTimeout(()=>{console.log("延迟零毫秒的定时任务1")},0)// 定时器setTimeout(()=>{// 状态为 fulfilled 的 PromisePromise.resolve().then(()=>{console.log("状态为 fulfilled 的 Promise 1")})},0)// 状态为 fulfilled 的 PromisePromise.resolve().then(()=>{console.log("状态为 fulfilled 的 Promise 2")})// 定时器setTimeout(()=>{console.log("延迟零毫秒的定时任务2")},0)console.log("主线程任务2")// 输出:// 主线程任务1// 主线程任务2// 状态为 fulfilled 的 Promise 2// 延迟零毫秒的定时任务1// 状态为 fulfilled 的 Promise 1// 延迟零毫秒的定时任务2
</script>

Web worker


ES6 以前,JS 是单线程的,所谓异步也都是依赖于浏览器内核的多线程机制,而不是 JS 本身具有多线程特性,这就导致能支持的异步操作很少(定时器线程,事件线程,Ajax线程),ES6 以后新增的 Web worker 功能让 JS 真正的拥有了多线程特性,但是 Web work 创建的子线程有一些使用限制。

限制描述
同源限制子线程的 JS 脚本,必须和主线程的脚本文件同源
DOM限制不能对页面元素操作,包括不能使用弹出框等,可以理解为 JS 子线程无法使用渲染进程的渲染引擎

1. 基本语法

主线程脚本

<script>// 用来创建并启动一个 JS 子线程,参数为 JS 脚本文件 URLconst work = new Worker("./worker.js")// 给子线程发送数据,参数任何类型都可以work.postMessage('发送给子线程的消息')// 监听子线程是否有消息返回,event 为事件对象,event.data 可以获取子线程返回的数据work.onmessage = (event)=>{console.log('接收到的子线程处理结果:' + event.data)// 关闭子线程work.terminate();}
</script>

子线程脚本文件 worker.js

console.log('子线程启动')
// 监听主线程是否有消息发送过来,event 为事件对象,event.data 可以获取主线程发送的数据
addEventListener('message', (event) => {console.log('子线程接到消息:' + event.data)// 向主线程发送数据postMessage('处理完成!')// 关闭子线程自身(和 terminate 功能一样,防止主线程调用后忘记关闭,所以此处也写一份 )close()
})

2. 同一文件内使用 Web worker

先说思路,主线程和子线程要分别写在不同的 script 标签对儿中,然后主线程读取子线程标签对儿中的内容,并将其创建成 Blob 类型(BlobFile 类型的父类,所以 Blob 也可以简单理解为文件类型),然后对该文件对象(Blob)生成 url,最后 worker 访问该 url

再说需要注意的东西:
(1) 子线程的 script 脚本要写在主线程 script 脚本之前,防止主线程中读取不到子线程的 script 标签
(2) 子线程的 script 标签的 type 属性,要给一个 type 规定的合法值以外的值(本人喜欢给 web-worker),如果是
合法值,就会被 JS 线程 ( JS 引擎 ) 识别,然后会直接运行其内容,而我们预想的执行时机是主线程调用后执行

<!-- 子线程脚本 -->
<script id="worker" type="web-worker">console.log('子线程启动')// 监听主线程是否有消息发送过来,event 为事件对象,event.data 可以获取主线程发送的数据addEventListener('message', (event) => {console.log('子线程接到消息:' + event.data)// 向主线程发送数据postMessage('处理完成!')// 关闭子线程自身(和 terminate 功能一样,防止主线程调用后忘记关闭,所以此处也写一份 )close()})
</script>
<!-- 主线程脚本 -->
<script>// 读取子线程脚本内容, 将其转换成二进制类型(Blob 是 File 类型的父类,所以 Blob 也可以理解为类文件类型)var blob = new Blob([document.querySelector("#worker").textContent]);// 针对 blob 文件生成 URL var url = window.URL.createObjectURL(blob);// 用来创建并启动一个 JS 子线程,参数为生成的 URLconst work = new Worker(url)// 给子线程发送数据,参数任何类型都可以work.postMessage('发送给子线程的消息')// 监听子线程是否有消息返回,event 为事件对象,event.data 可以获取子线程返回的数据work.onmessage = (event)=>{console.log('接收到的子线程处理结果:' + event.data)// 关闭子线程work.terminate();}
</script>

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/496265.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Elasticsearch-脚本查询

脚本查询 概念 Scripting是Elasticsearch支持的一种专门用于复杂场景下支持自定义编程的强大的脚本功能&#xff0c;ES支持多种脚本语言&#xff0c;如painless&#xff0c;其语法类似于Java,也有注释、关键字、类型、变量、函数等&#xff0c;其就要相对于其他脚本高出几倍的性…

蓝牙BLE开发——解决iOS设备获取MAC方式

解决iOS设备获取MAC方式 uniapp 解决 iOS 获取 MAC地址&#xff0c;在Android、iOS不同端中互通&#xff0c;根据MAC 地址处理相关的业务场景&#xff1b; 文章目录 解决iOS设备获取MAC方式监听寻找到新设备的事件BLE工具效果图APP监听设备返回数据解决方式ArrayBuffer转16进制…

高仿CSDN编辑器,前端博客模板

高仿CSDN编辑器纯前端模板&#xff0c;使用的js、html、vue、axios等技术&#xff0c;网络请求库已进行封装&#xff0c;可以按需调整界面,需要源码联系(4k左右)。 1.支持代码高亮 2.支持目录点击定位 3.支持文件上传、图片上传&#xff08;需要自己写后端接口&#xff09; 4.M…

01 - 初识 Spring

初识Spring 企业级应用 企业级应用是指那些为商业组织、⼤型企业而创建并部署的解决⽅案及应用。这些⼤型的企业级应用结构复 杂、涉及的外部资源众多&#xff0c;事务密集&#xff0c;数据规模⼤&#xff0c;用户数量多&#xff0c;有较强的安全性考虑和较⾼的性能要求。 …

后端开发如何高效使用 Apifox?

Apifox 是一个 API 协作开发平台&#xff0c;后端、前端、测试都可以使用 Apifox 来提升团队的工作效率。对于后端开发者而言&#xff0c;Apifox 的核心功能主要包括四个模块&#xff1a;调用 API、定义 API、开发与调试 API 以及生成 API 文档。本文将详细介绍后端开发人员如何…

解决 vue3 中 echarts图表在el-dialog中显示问题

原因&#xff1a; 第一次点开不显示图表&#xff0c;第二次点开虽然显示图表&#xff0c;但是图表挤在一起&#xff0c;页面检查发现宽高只有100px,但是明明已经设置样式宽高100% 这可能是由于 el-dialog 还没有完全渲染完成&#xff0c;而你的 echarts 组件已经开始尝试渲染图…

PyQt实战——随机涂格子的特色进度条(十一)

系类往期文章&#xff1a; PyQt5实战——多脚本集合包&#xff0c;前言与环境配置&#xff08;一&#xff09; PyQt5实战——多脚本集合包&#xff0c;UI以及工程布局&#xff08;二&#xff09; PyQt5实战——多脚本集合包&#xff0c;程序入口QMainWindow&#xff08;三&…

tryhackme-Cyber Security 101-Linux Shells(linux命令框)

目的&#xff1a;了解脚本和不同类型的 Linux shell。 任务1&#xff1a;Introduction to Linux Shells&#xff08;Linux Shell 简介&#xff09; 作为操作系统的常规用户&#xff0c;我们都广泛使用图形用户界面 &#xff08;GUI&#xff09; 来执行大多数操作。只需点击几…

全面Kafka监控方案:从配置到指标

文章目录 1.1.监控配置1.2.监控工具1.3.性能指标系统相关指标GC相关指标JVM相关指标Topic相关指标Broker相关指标 1.4.性能指标说明1.5.重要指标说明 1.1.监控配置 开启JMX服务端口&#xff1a;kafka基本分为broker、producer、consumer三个子项&#xff0c;每一项的启动都需要…

VirtualBox下ubuntu23.04使用主机串口以及使用 minicom 进行串口调试

VirtualBox下ubuntu23.04使用主机串口以及使用 minicom 进行串口调试 一、打开设备管理器看主机&#xff08;Window系统&#xff09;是否识别出串口&#xff0c;我这边显示的串行通信端口是COM3 二、打开VirtualBox&#xff0c;设置串口和USB设备 串口设置&#xff1a; 启用…

解决PDF.js部署到IIS服务器上后报错mjs,.ftl 404 (Not Found)

一、报错问题描述&#xff1a;部署到IIS服务器上后,浏览器控制台报错报错mjs,.ftl 404 (Not Found)&#xff0c;pdf也浏览不了 二、解决方法&#xff1a;在IIS服务器添加MIME类型 将下面类型添加即可 .mjs application/javascript .ftl application/octet-stream保存后&…

Jmeter下载安装配置教程(多版本)

目录 一、介绍 JMeter的主要特点&#xff1a; 使用场景&#xff1a; 二、下载 (一)下载最新版本 (二)下载历史版本 (三)配置环境变量 ​(四)查看版本 (五)启动方式 一、介绍 Apache JMeter 是一款开源的性能测试工具&#xff0c;主要用于对各种服务进行负载测试和性…

PTA数据结构编程题7-1最大子列和问题

我参考的B站up的思路 题目 题目链接 给定K个整数组成的序列{ N 1 ​ , N 2 ​ , …, N K ​ }&#xff0c;“连续子列”被定义为{ N i ​ , N i1 ​ , …, N j ​ }&#xff0c;其中 1≤i≤j≤K。“最大子列和”则被定义为所有连续子列元素的和中最大者。例如给定序列{ -2, 1…

【路径规划】原理及实现

路径规划&#xff08;Path Planning&#xff09;是指在给定地图、起始点和目标点的情况下&#xff0c;确定应该采取的最佳路径。常见的路径规划算法包括A* 算法、Dijkstra 算法、RRT&#xff08;Rapidly-exploring Random Tree&#xff09;等。 目录 一.A* 1.算法原理 2.实…

在 Vue3 项目中实现计时器组件的使用(Vite+Vue3+Node+npm+Element-plus,附测试代码)

一、概述 记录时间 [2024-12-26] 本文讲述如何在 Vue3 项目中使用计时器组件。具体包括开发环境的配置&#xff0c;ViteVue 项目的创建&#xff0c;Element Plus 插件的使用&#xff0c;以及计时器组件的创建和使用。 想要直接实现计时器组件&#xff0c;查看文章的第四部分。…

简单园区网拓扑实验

1.实验拓扑 2.实验要求 1、按照图示的VLAN及IP地址需求&#xff0c;完成相关配置 2、要求SW1为VLAN 2/3的主根及主网关 SW2为vlan 20/30的主根及主网关 SW1和SW2互为备份 3、可以使用super vlan 4、上层通过静态路由协议完成数据通信过程 5、AR1为企业出口路由器 6、要求全网可…

jetson Orin nx + yolov8 TensorRT 加速量化 环境配置

参考【Jetson】Jetson Orin NX纯系统配置环境-CSDN博客 一 系统环境配置&#xff1a; 1.更换源&#xff1a; sudo vi /etc/apt/sources.list.d/nvidia-l4t-apt-source.list2.更新源&#xff1a; sudo apt upgradesudo apt updatesudo apt dist-upgrade sudo apt-get updat…

音视频入门基础:MPEG2-TS专题(22)——FFmpeg源码中,获取TS流的音频信息的实现

音视频入门基础&#xff1a;MPEG2-TS专题系列文章&#xff1a; 音视频入门基础&#xff1a;MPEG2-TS专题&#xff08;1&#xff09;——MPEG2-TS官方文档下载 音视频入门基础&#xff1a;MPEG2-TS专题&#xff08;2&#xff09;——使用FFmpeg命令生成ts文件 音视频入门基础…

MySQL45讲 第三十六讲 为什么临时表可以重名?——阅读总结

文章目录 MySQL45讲 第三十六讲 为什么临时表可以重名&#xff1f;——阅读总结一、引言二、临时表与内存表的区别&#xff08;一&#xff09;内存表&#xff08;二&#xff09;临时表 三、临时表的特性&#xff08;一&#xff09;可见性与生命周期&#xff08;二&#xff09;与…

MATLAB符号计算-符号表达式基础运算操作

1.1.2符号变量取值域的限定 默认复数域 【例1-1-2】解不等式 1.1.3创建符号表达式 对符号对象进行各种运算&#xff08;算术运算、关系运算、逻辑运算&#xff09;&#xff0c;即可创建符号表达式。 1.算术运算与转置 【例1-1-3】 f5是f4的共轭转置 f6是f4的转置 2.关系…