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

devtools/2024/12/27 21:31:35/

文章目录

  • 异步
    • 代码异步执行概念
    • 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>javascript">(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>javascript">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>javascript">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>javascript">// 用来创建并启动一个 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

javascript">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">javascript">console.log('子线程启动')// 监听主线程是否有消息发送过来,event 为事件对象,event.data 可以获取主线程发送的数据addEventListener('message', (event) => {console.log('子线程接到消息:' + event.data)// 向主线程发送数据postMessage('处理完成!')// 关闭子线程自身(和 terminate 功能一样,防止主线程调用后忘记关闭,所以此处也写一份 )close()})
</script>
<!-- 主线程脚本 -->
<script>javascript">// 读取子线程脚本内容, 将其转换成二进制类型(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.ppmy.cn/devtools/145927.html

相关文章

FreeSwitch中启用WebRTC

在FreeSwitch中启用WebRTC需要进行一系列配置。以下是详细的步骤&#xff1a; 1. 安装必要的依赖&#xff1a; 确保安装了支持WebRTC的依赖库&#xff0c;如libsrtp。 2. 配置SIP Profile&#xff1a; 编辑 conf/sip_profiles/internal.xml 文件&#xff0c;添加或修改以下内…

使用c#制作坐标

1、建立坐标 2、坐标系的放大缩小 3、标定刻度 4、实时显示鼠标在坐标系上的坐标 using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Drawing.Drawing2D; using System.Linq; using S…

深圳南柯电子|医疗设备EMC测试整改:确保电磁安全的合规之路

在现代医疗领域&#xff0c;电子设备的广泛应用极大地提升了医疗服务的效率和质量。然而&#xff0c;这些设备在复杂的电磁环境中运行时&#xff0c;可能会受到各种电磁干扰&#xff08;EMI&#xff09;&#xff0c;同时也可能成为干扰源&#xff0c;影响其他设备的正常运行。因…

Go语言zero项目服务恢复与迁移文档

## 一. 服务器环境配置 在迁移和配置 项目时&#xff0c;首先需要确保服务器环境正确配置。以下是配置步骤&#xff1a; ### 1. 安装 Go 语言环境 首先&#xff0c;确保 Go 语言环境已经安装&#xff0c;并且配置正确。执行以下步骤&#xff1a; # 下载 Go 语言安装包 wge…

管理面板Ajenti的在Windows10下Ubuntu24.04/Ubuntu22.04里的配置管理

Ajenti是一款基于Web的开源系统管理控制面板&#xff0c;可用于通过Web浏览器&#xff0c;管理远程系统管理性任务&#xff0c;这一点与 Webmin模块 非常相似。 Ajenti是一款功能非常强大的轻型工具&#xff0c;它提供了快速的、反应灵敏的Web界面&#xff0c;可用于管理小型服…

【AI系统】LLVM 前端和优化层

LLVM 前端和优化层 在上一篇文章讲到了 LLVM 的 IR 贯穿了 LLVM 编译器的全生命周期&#xff0c;里面的每一个箭头都是一个 IR 的过程&#xff0c;这个就是整体 LLVM 最重要的核心概念。 有了 LVM IR 之后这并不意味着 LLVM 或者编译器的整个 Pipeline 都是使用一个单一的 IR…

SQL进阶技巧:如何计算加油站问题? | LeetCode 134. 加油站

目录 0 问题描述 1 数据准备 2 问题分析 计算每个加油站剩余油量(当前油量减去到下一个加油站消耗的油量)

Java爬虫技术:按关键字搜索VIP商品详情

在数字化时代&#xff0c;电子商务平台的竞争日益激烈&#xff0c;而精准的数据采集和分析成为了企业获取竞争优势的关键。对于电商平台而言&#xff0c;能够根据用户输入的关键字快速搜索并展示VIP商品的详细信息&#xff0c;不仅能够提升用户体验&#xff0c;还能够增加销售机…