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 的 Promise
  Promise.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 的 Promise
    Promise.resolve().then(()=>{
      console.log("状态为 fulfilled 的 Promise 1")
    })
  },0)
  // 状态为 fulfilled 的 Promise
  Promise.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 脚本文件 URL
  const 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 子线程,参数为生成的 URL
  const work = new Worker(url)
  // 给子线程发送数据,参数任何类型都可以
  work.postMessage('发送给子线程的消息')
  // 监听子线程是否有消息返回,event 为事件对象,event.data 可以获取子线程返回的数据
  work.onmessage = (event)=>{
    console.log('接收到的子线程处理结果:' + event.data)
    // 关闭子线程
    work.terminate();
  }
</script>

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

机器学习(二)-简单线性回归

文章目录 1. 简单线性回归理论2. python通过简单线性回归预测房价2.1 预测数据2.2导入标准库2.3 导入数据2.4 划分数据集2.5 导入线性回归模块2.6 对测试集进行预测2.7 计算均方误差 J2.8 计算参数 w0、w12.9 可视化训练集拟合结果2.10 可视化测试集拟合结果2.11 保存模型2.12 …

Java字符串操作利器:StringBuffer与StringBuilder类详解

在处理字符串变更时&#xff0c;StringBuffer和StringBuilder类是优选工具。与String类不同&#xff0c;StringBuffer和StringBuilder允许对象被多次修改&#xff0c;而不会生成新的未使用对象。 StringBuilder类自Java 5起引入&#xff0c;其与StringBuffer的主要区别在于Stri…

软件确认测试报告的内容和作用简析

软件确认测试报告是对软件确认测试过程及结果的正式记录&#xff0c;是评估软件质量的重要依据。它不仅对开发团队起到反馈作用&#xff0c;更是决策层判断软件是否可以交付的重要参考。 一、软件确认测试报告包括的内容   1、测试目的&#xff1a;明确此次测试的目的和所要…

结构体(初阶)

结构体&#xff1a; 结构体类型的声明 结构体初始化 结构成员访问 结构体传参 1.结构体的声明 1.1结构的基础知识 结构是一些值的集合&#xff0c;这些值称为成员变量。结构的每个成员可以是不同类型的变量。 1.2结构的声明 struct tag { member - list; }variable-lis…

详解VHDL如何编写Testbench

1.概述 仿真测试平台文件(Testbench)是可以用来验证所设计的硬件模型正确性的 VHDL模型&#xff0c;它为所测试的元件提供了激励信号&#xff0c;可以以波形的方式显示仿真结果或把测试结果存储到文件中。这里所说的激励信号可以直接集成在测试平台文件中&#xff0c;也可以从…

React 第二十节 useRef 用途使用技巧注意事项详解

简述 useRef 用于操作不需要在视图上渲染的属性数据&#xff0c;用于访问真实的DOM节点&#xff0c;或者React组件的实例对象&#xff0c;允许直接操作DOM元素或者是组件&#xff1b; 写法 const inpRef useRef(params)参数&#xff1a; useRef(params)&#xff0c;接收的 …

SQL子查询和having实例

有2个表如下&#xff1b;一个是站点信息&#xff0c;一个是站点不同时间的访问量&#xff0c; 现在要获取总访问量大于200的网站&#xff1b; 先执行如下sql&#xff0c;不包括having子句看一下&#xff0c;获得的是所有站点的总访问量&#xff1b; 这应是一个子查询&#xf…

【seatunnel】数据同步软件安装

【seatunnel】数据同步软件安装 下载 wget https://dlcdn.apache.org/seatunnel/2.3.8/apache-seatunnel-2.3.8-bin.tar.gz wget https://dlcdn.apache.org/seatunnel/seatunnel-web/1.0.2/apache-seatunnel-web-1.0.2-bin.tar.gz1、安装seatunnel Server 解压 tar zxvf ap…

散斑/横向剪切/迈克尔逊/干涉条纹仿真技术分析

摘要 本博文提供了多种数据类型的干涉条纹仿真&#xff0c;并展示了它们对应的散斑干涉条纹。还分别给出了横向剪切干涉以及剪切散斑干涉条纹的仿真。 一、迈克尔逊干涉与散斑干涉仿真 下图为干涉条纹与对应的散斑干涉条纹的仿真示意图。其中&#xff0c;干涉条纹可认为是源…

如何通过采购管理系统实现智能化采购?

随着人工智能、大数据等技术的快速发展&#xff0c;采购管理逐步迈入智能化时代。智能化采购不仅提升了效率&#xff0c;还为企业提供了更精准的采购决策支持。本文将从智能化采购的优势出发&#xff0c;探讨采购管理系统如何助力企业实现这一目标。 文中用到的采购管理系统&a…

【论文阅读笔记】IC-Light

SCALING IN-THE-WILD TRAINING FOR DIFFUSION-BASED ILLUMINATION HARMONIZATION AND EDITING BY IMPOSING CONSISTENT LIGHT TRANSPORT 通过施加一致的光线传输来扩展基于扩散模型的真实场景光照协调与编辑训练 前言摘要引言相关工作基于学习的基于扩散模型的外观和光照操纵光…

594: Maximum Tape Utilization Ratio

解法&#xff1a; 对于该题有以下错误&#xff08;敬希评论区指正 1.dp定义在全局会wa struct node {int count; // 当前容量下能够存储的程序数量int sum; // 当前容量下所占用的磁带长度vector<int> path; // 当前容量下选择的程序的路径&#xff08;存放的程序…

流量主微信小程序工具类去水印

工具类微信小程序流量主带后台管理&#xff0c;可开通广告&#xff0c;带自有后台管理&#xff0c;不借助第三方接口 介绍 支持抖音&#xff0c;小红书&#xff0c;哔哩哔哩视频水印去除&#xff0c;功能实现不借助第三方平台。可实现微信小程序流量主广告变现功能&#xff0c…

04软件测试需求分析案例-用户登录

通读文档&#xff0c;提取信息&#xff0c;提出问题&#xff0c;整理为需求。 从需求规格说明、设计说明、配置说明等文档获取原始需求&#xff0c;通读原始需求&#xff0c;分析有哪些功能&#xff0c;每种功能要完成什么业务&#xff0c;业务该如何实现&#xff0c;业务逻辑…

DX12 快速教程(2) —— 渲染天蓝色窗口

快速导航 新建项目 "002-DrawSkyblueWindow"DirectX 12 入门1. COM 技术&#xff1a;DirectX 的中流砥柱什么是 COM 技术COM 智能指针 2.创建 D3D12 调试层设备&#xff1a;CreateDebugDevice什么是调试层如何创建并使用调试层 3.创建 D3D12 设备&#xff1a;CreateD…

《计算机组成及汇编语言原理》阅读笔记:p116-p120

《计算机组成及汇编语言原理》学习第 7 天&#xff0c;p116-p120 总结&#xff0c;总计 5 页。 一、技术总结 1.CPU优化 (1)increase overall performance number 例如&#xff1a;16位电脑提升到32位电脑。 (2)multiprocessing One way to make computers more useful i…

【蓝桥杯每日一题】12.18

&#x1f3dd;️专栏&#xff1a; 【蓝桥杯备篇】 &#x1f305;主页&#xff1a; f狐o狸x 从今天开始&#xff0c;笨狐狸&#xff0c;啊呸&#xff0c;本狐狸要开始漫长的蓝桥杯备战啦&#xff0c;将会长期更新每日一题这个专栏&#xff0c;直到蓝桥杯结束&#xff0c;各位一起…

Python 写的 智慧记 进销存 辅助 程序 导入导出 excel 可打印

图样&#xff1a; 就可以导入了 上代码 import tkinter as tk from tkinter import ttk import sqlite3 from datetime import datetime from tkinter import messagebox, filedialog import pandas as pd import reclass OrderSystem:def __init__(self, root):self.root r…

【机器学习案列】车牌自动识别系统:基于YOLO11的高效实现

&#x1f9d1; 博主简介&#xff1a;曾任某智慧城市类企业算法总监&#xff0c;目前在美国市场的物流公司从事高级算法工程师一职&#xff0c;深耕人工智能领域&#xff0c;精通python数据挖掘、可视化、机器学习等&#xff0c;发表过AI相关的专利并多次在AI类比赛中获奖。CSDN…

SpringBoot(二)—— yaml配置文件

接上篇&#xff0c;我们对SpringBoot有了基本的了解&#xff0c;接下来探究配置文件。 目录 二、配置文件 1. SpringBoot热部署 2. 配置文件 2.1 配置文件的作用 2.2 YAML 配置文件 2.3 YAML 与 XML 比较 3. YAML语法 3.1 键值对 3.2 值的写法 3.3 对象/Map&#x…