前端面试题整理-前端异步编程

news2024/12/22 22:15:45

1. 进程、线程、协程的区别

在并发编程领域,进程、线程和协程是三个核心概念,它们在资源管理、调度和执行上有着本质的不同。

首先,进程是操作系统进行资源分配和调度的独立单位(资源分配基本单位),每个进程拥有自己的内存空间,这使得进程间相互隔离,从而提高了系统的稳定性。然而,这种隔离也带来了进程间通信的复杂性,需要通过特定的机制如管道、共享内存等来实现。进程的创建和销毁涉及到系统资源的分配和回收,因此开销相对较大。适用于需要高隔离性和独立资源的应用,比如浏览器的多进程架构,每个标签页运行在独立的进程中。

接着,线程作为进程的执行单元(CPU调度基本单位),共享进程的资源,包括内存空间,这使得线程间的通信更为简便。线程的创建和销毁开销相对较小,但仍然需要操作系统的调度,因此线程的并发执行能力受到操作系统调度策略和硬件资源的限制。适用于需要高效并发处理的场景,如Web服务器的请求处理、前端的异步操作(如Web Workers)。

最后,协程是一种更轻量级的并发机制,它允许程序在不同的执行点挂起和恢复,通常由程序内部进行调度,而不是依赖操作系统。协程特别适合处理I/O密集型任务,因为它们可以在等待I/O操作时挂起,从而提高资源利用率。协程的创建和销毁开销非常小,这使得它们在处理大量并发任务时非常有用,尤其是在Python、Go等语言中,协程被广泛用于提高并发性能。适用于需要大量并发但不需要多线程的场景,如JavaScript中的异步编程(Promise、async/await)、Python的异步I/O。

2. 说说他们的通信方式

进程、线程和协程之间的通信方式各有特点,下面分别介绍它们的通信机制:

(1) 进程通信(Inter-Process Communication, IPC)

  • 管道(Pipes):允许一个进程的输出成为另一个进程的输入。常用于父子进程间的单向或双向通信,简单且高效。
  • 命名管道(Named Pipes):类似于管道,但是它们在文件系统中有名字,可以跨会话使用。
  • 消息队列(Message Queues):允许进程以消息的形式交换数据,消息被存储在队列中直到被接收。支持异步通信,允许消息在进程间传递,适合复杂的消息传递场景。
  • 信号(Signals):一种由操作系统提供的异步通信机制,用于通知进程某个事件已经发生,适合简单的通知机制。
  • 共享内存(Shared Memory):允许两个或多个进程共享一个给定的存储区。速度最快的通信方式,进程共享一块内存区域,但需要同步机制来避免竞争条件。
  • 套接字(Sockets):支持不同主机之间的进程通信,可以是TCP/IP或UDP/IP协议。不仅用于网络通信,也可以用于本地进程间通信,灵活且强大。
  • 信号量(Semaphores)互斥锁(Mutexes):用于控制对共享资源的访问,防止多个进程同时访问同一资源。

(2) 线程间通信

  • 共享内存:由于线程共享相同的内存空间,它们可以直接通过读取和修改共享变量来通信,效率高。
  • 互斥锁(Mutexes):用于同步线程对共享资源的访问,防止数据竞争。
  • 条件变量(Condition Variables):允许线程在某个条件为真之前挂起,并在条件变为真时被唤醒。线程可以等待特定条件满足后继续执行,适合复杂的同步场景。
  • 信号量(Semaphores):用于控制对共享资源的访问,也可以用于线程间的同步,适合计数资源的同步。
  • 屏障(Barriers):同步机制,用于等待一定数量的线程都到达某个点后再继续执行。

(3) 协程间通信

  • 通道(Channels):在支持协程的语言中(如Go),通道是一种同步通信机制,允许协程通过发送和接收数据来通信,通常用于协程间的同步和数据交换。类似于管道,用于协程间传递消息或数据,直观且易于使用。
  • 共享变量:类似于线程,协程也可以通过共享变量来通信,但需要小心处理同步问题,以避免竞态条件。
  • 事件循环(Event Loop):在JavaScript中,协程(如通过async/await实现的异步函数)通常依赖于事件循环来管理异步操作,事件循环负责调度和执行异步任务。
  • Future/Promise:用于处理异步操作的结果,提供一种优雅的方式来处理协程间的结果传递。

3. js 是线程还是进程

在浏览器环境中,JavaScript 是单线程的。它在一个线程中执行代码,处理事件和更新用户界面。JavaScript 使用事件循环来管理异步操作。事件循环允许 JavaScript 处理异步任务,如网络请求或定时器,而不会阻塞主线程。虽然 JavaScript 本身是单线程的,但可以通过 Web Workers 创建多线程环境。Web Workers 允许在后台线程中运行代码,从而避免阻塞主线程。这种设计使得 JavaScript 能够高效地管理用户界面交互和后台任务。

4. js 事件循环机制

原理

JS是单线程的,为了防止代码阻塞,把任务分成同步任务和异步任务。
同步任务放入JS引擎直接执行,原地等待结果,其任务放入执行栈中按顺序执行;而异步任务需要放入宿主环境(浏览器、Node),不用原地等待结果,比较耗时,其任务放在任务队列中。
首先,执行栈中的同步任务会按顺序执行。当执行栈为空时,事件循环会检查任务队列。执行所有的微任务。如果调用栈为空,则从任务队列中取出一个宏任务并执行。执行完毕后,事件循环再次检查任务队列,重复这个过程。
在 JavaScript 中,任务可以分为同步任务、宏任务(Task),和微任务(Microtask)。以下是一些常见的例子:

同步任务

这些任务会立即执行,按顺序依次完成,不会被中断。

  • 普通的变量声明和赋值
  • 函数调用
  • console.log
宏任务(Task)

这些任务会被添加到任务队列中,等到主线程空闲时执行。

  • 整个脚本(初始执行)(指从头到尾执行一段 JavaScript 代码。它是事件循环中第一个被处理的宏任务)
  • setTimeout
  • setInterval
  • I/O 操作(读取文件或发送网络请求)
  • UI 渲染事件
  • setImmediate(Node.js)
微任务(Microtask)

这些任务会在当前宏任务执行结束后立即执行,在下一个宏任务开始前完成。

  • Promise 的回调函数(then, catch, finally
  • process.nextTick(Node.js)
  • MutationObserver
  • async/awaitawait之后的代码都看作微任务!(因为 await 会暂停函数的执行,并让出事件循环。在 await 的 Promise 解决后,函数会继续执行,后续代码作为微任务在当前宏任务完成后执行。)
执行顺序
  1. 执行同步任务。
  2. 当前宏任务执行完毕后,执行所有微任务。
  3. 执行下一个宏任务。

这种机制确保了同步任务优先执行,微任务在宏任务之间迅速处理,保持高效的异步操作。

举例

(1) 同步函数执行顺序

对于同步函数 f1()f2()

function f1(){
  for(let i=0; i<200; i++){}
}

function f2(){
  for(let i=0; i<300; i++){}
}

f1();
f2();

执行顺序是严格按照代码的顺序进行的:

  1. f1() 完成后才会执行 f2()
  2. 因为它们都是同步代码,所以没有异步调度的干扰。
(2) 异步函数执行顺序

对于异步函数 promise1()promise2()

async function promise1(){
  await xx;
  console.log(1);
}

async function promise2(){
  await xx;
  console.log(2);
}

promise1();
promise2();

假设 xx 是一个返回 Promise 的操作,执行顺序如下:

  1. promise1()promise2() 被调用,返回的 Promise 进入微任务队列。
  2. await xx 处,函数会暂停,控制权返回到事件循环。
  3. 一旦主线程的同步代码执行完毕,事件循环会处理微任务队列中的任务。
  4. await xx 完成后,console.log(1)console.log(2) 分别被放入微任务队列。
  5. 因为 promise1() 先调用,所以 console.log(1) 会先执行,接着是 console.log(2)

因此,输出结果是:

1
2
  • await 会暂停函数的执行,直到 Promise 解决为止。
  • async/await 使得异步代码看起来像同步代码,但实际执行顺序依赖于事件循环和微任务队列。
  • 微任务(例如 await 的处理)会在当前宏任务结束后立即执行。
(3) 混合任务
async function promise1() {
  console.log('A');
  await Promise.resolve(console.log('C'));
  console.log(1);
}

async function promise2() {
  console.log('B');
  await Promise.resolve(console.log('D'));
  console.log(2);
}

promise1();
promise2();

在这个代码片段中:
同步任务:

  • console.log('A')
  • console.log('C')
  • console.log('B')
  • console.log('D')
    这些会按照顺序立即执行。

微任务:

  • console.log(1)(作为 promise1 中的 await 后的代码)
  • console.log(2)(作为 promise2 中的 await 后的代码)
    这些会在当前宏任务完成后,作为微任务执行。

宏任务:

  • 每个 promise1()promise2() 的调用都是一个宏任务,但在这个上下文中,主要关注的是事件循环的执行顺序。

执行顺序:

  • 同步任务先执行,打印 ACBD
  • 然后执行微任务队列中的 console.log(1)console.log(2)

5. js 中的 async / defer 属性?平时用那种方式多?(参考掘金《「2021」高频前端面试题汇总之HTML篇》)

在这里插入图片描述
其中蓝色代表js脚本网络加载时间,红色代表js脚本执行时间,绿色代表html解析。

  • 没有deferasync 加载和执行都会阻塞html的解析

deferasync属性都是去异步加载外部的JS脚本文件,它们都不会阻塞页面的解析,区别:

  • defer 异步加载JS文件,不会立即执行,不阻塞html解析,会在解析完成后按顺序执行
  • async 异步加载和执行JS文件,不会阻塞html解析,下载完就执行,没有执行顺序

平时用那种方式多?
defer多。使用 defer 进行优化,不阻塞 html 解析;执行会按照顺序来,保证了正确性。

6. 说说异步编程中的 async / await

  • async: 用于定义一个异步函数,函数返回一个 Promise。即使没有显式返回 Promise,函数也会自动将返回值包装成一个 Promise

  • await: 用于暂停异步函数的执行,等待一个 Promise 完成,并返回其解析值。await 只能在 async 函数中使用。
    用法:

    async function fetchData() {
    try {
    const response = await fetch(‘https://api.example.com/data’);
    const data = await response.json();
    console.log(data);
    } catch (error) {
    console.error(‘Error:’, error);
    }
    }

工作原理:

  • 当函数被调用时,会立即返回一个 Promise
  • 函数内部遇到 await 时,会暂停执行,直到 Promise 解决或拒绝。
  • await 后的表达式会被转换为 Promise.resolve()

优势:

  • 简化代码: 使异步代码看起来更像同步代码,易于理解和维护。
  • 错误处理: 可以使用 try/catch 语句来处理异步操作中的错误。

注意事项:

  • await 只能在 async 函数中使用。
  • 多个 await 操作会导致顺序执行,可能影响性能。可以使用 Promise.all() 来并行执行多个异步操作。

示例对比:
Promise then/catch:

fetch('https://api.example.com/data')
  .then(response => response.json())
  .then(data => console.log(data))
  .catch(error => console.error('Error:', error));

Async/Await:

async function fetchData() {
  try {
    const response = await fetch('https://api.example.com/data');
    const data = await response.json();
    console.log(data);
  } catch (error) {
    console.error('Error:', error);
  }
}

结论:
async/await 是基于 Promise 的语法糖(在编程语言中,为了提高可读性和简洁性而提供的语法特性),使得异步代码更简洁和易读,是现代 JavaScript 开发中的重要工具。

7. await 会阻塞js线程加载执行吗

await 不会阻塞 JavaScript 线程的执行。它只是暂停当前异步函数的执行,等待 Promise 完成(被解决或拒绝)。在此期间,JavaScript 事件循环仍然可以处理其他任务,如用户交互、渲染等。

async function example() {
  console.log('Start');
  const result = await new Promise(resolve => setTimeout(() => resolve('Done'), 1000));
  console.log(result);
}

example();
console.log('This runs while waiting');

// 输出
Start
This runs while waiting
Done

8. 说说web worker

待看文章:一文彻底学会使用web worker

什么是 Web Worker

Web Worker 是一种在浏览器中创建后台线程的方法,用于执行复杂或耗时的 JavaScript 任务,而不会阻塞主线程。这有助于保持用户界面的流畅性。

为什么使用 Web Worker?
  1. 提高性能: 在处理密集计算任务时,Web Worker 可以防止页面卡顿。
  2. 增强用户体验: 通过避免长时间的 UI 阻塞,用户界面可以保持响应。
Web Worker 的使用场景
  • 复杂计算: 比如图像处理、数据分析、加密操作等。
  • 长时间运行任务: 如大数据处理、文件解析等。
示例

在一个项目中,我需要对用户上传的图片进行滤镜处理。由于处理过程较为复杂,我使用了 Web Worker 来避免阻塞主线程。

// worker.js
self.onmessage = function(event) {
  const imageData = event.data;
  // 进行复杂的图像处理
  const processedData = applyFilter(imageData);
  self.postMessage(processedData);
};

// 主线程
const worker = new Worker('worker.js');

worker.onmessage = function(event) {
  const processedData = event.data;
  // 显示处理后的图像
  displayImage(processedData);
};

worker.postMessage(originalImageData);
注意
  • 无法访问 DOM: Web Worker 不能直接操作 DOM。
  • 通信成本: 主线程和 Worker 之间通过消息传递进行通信,可能会有一定的延迟。

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

ARM学习(38)多进程多线程之间的通信方式

ARM学习(38)ARM学习(38)多进程多线程之间的通信方式 一、问题背景 笔者在调试模拟器的时候,碰到进程间通信的问题,一个进程在等另外一个进程ready的时候,迟迟等不到,然后通过调试发现,另外一个进程变量已经变化了,但是当前进程变量没变化,需要了解进程间通信的方式…

群晖利用acme.sh自动申请证书并且自动重载证书的问题解决

前言 21年的时候写了一个在群晖&#xff08;黑群晖&#xff09;下利用acme.sh自动申请Let‘s Encrypt的脚本工具 群晖使用acme自动申请Let‘s Encrypt证书脚本&#xff0c;自动申请虽然解决了&#xff0c;但是自动重载一直是一个问题&#xff0c;本人也懒&#xff0c;一想到去…

level2逐笔委托查询接口

沪深逐笔委托队列查询 前置步骤 分配数据库服务器 查询模板 以下是沪深委托队列查询的请求模板&#xff1a; http://<数据库服务器>/sql?modeorder_book&code<股票代码>&offset<offset>&token<token>查询参数说明 参数名类型说明mo…

delve调试环境搭建—golang

原文地址&#xff1a;delve调试环境搭建—golang – 无敌牛 欢迎参观我的个人博客&#xff1a;无敌牛 – 技术/著作/典籍/分享等 由于平时不用 IDE 开发环境&#xff0c;习惯在 linux终端vim 环境下开发&#xff0c;所以找了golang的调试工具&#xff0c;delve类似gdb的调试界…

PC寄存器(Program Counter Register) jvm

在JVM&#xff08;Java虚拟机&#xff09;中&#xff0c;PC寄存器&#xff08;Program Counter Register&#xff09;扮演着至关重要的角色&#xff0c;它是JVM执行引擎的核心组成部分之一。以下是PC寄存器在JVM中的具体角色和职责&#xff1a; 指令执行指针&#xff1a; PC寄存…

线性分类器(KNN,SVM损失,交叉熵损失,softmax)

KNN 工作机制 k-近邻算法的工作机制可以分为两个主要阶段&#xff1a;训练阶段和预测阶段。 训练阶段 在训练阶段&#xff0c;k-近邻算法并不进行显式的模型训练&#xff0c;而是简单地存储训练数据集。每个样本由特征向量和对应的标签组成。此阶段的主要任务是准备好数据&…

重拾设计模式--适配器模式

文章目录 适配器模式&#xff08;Adapter Pattern&#xff09;概述适配器模式UML图适配器模式的结构目标接口&#xff08;Target&#xff09;&#xff1a;适配器&#xff08;Adapter&#xff09;&#xff1a;被适配者&#xff08;Adaptee&#xff09;&#xff1a; 作用&#xf…

StarRocks:存算一体模式部署

目录 一、StarRocks 简介 二、StarRocks 架构 2.1 存算一体 2.2 存算分离 三、前期准备 3.1前提条件 3.2 集群规划 3.3 配置环境 3.4 准备部署文件 四、手动部署 4.1 部署FE节点 4.2 部署BE节点 4.3 部署CN节点&#xff08;可选&#xff09; 4.4 FE高可用…

找数字:JAVA

题目描述 试计算在区间1 到n 的所有整数中&#xff0c;数字x&#xff08;0 ≤ x ≤ 9&#xff09;共出现了多少次&#xff1f; 例如&#xff0c;在1到11 中&#xff0c;即在1、2、3、4、5、6、7、8、9、10、11 中&#xff0c;数字1 出现了4 次。 输入描述: 输入共1行&#xf…

AI的使用:结构化提示词

根据自己的使用&#xff0c;不断的完善自己的提示词。并且像程序版本一样管理和迭代自己的提示词&#xff0c;这样才能准确的按照自己的目的去使用AI。而为了更好的管理&#xff0c;我们在一开始使用的时候&#xff0c;就要有一个易于管理的定义&#xff0c;即&#xff1a;结构…

Netcat:网络中的瑞士军刀

免责声明&#xff1a;使用本教程或工具&#xff0c;用户必须遵守所有适用的法律和法规&#xff0c;并且用户应自行承担所有风险和责任。 文章目录 一、引言二、简述三、Netcat功能&#xff1f;四、参数选项五、Netcat 的常见功能六、高级用法多连接处理创建简单的代理 七、Netc…

VS Code Copilot 与 Cursor 对比

选手简介 VS Code Copilot&#xff1a;算是“老牌”编程助手了&#xff0c;虽然Copilot在别的编辑器上也有扩展&#xff0c;不过体验最好的还是VS Code&#xff0c;毕竟都是微软家的所以功能集成更好一些&#xff1b;主要提供的是Complete和Chat能力&#xff0c;也就是代码补全…

28、基于springboot的房屋租赁系统

房屋是人类生活栖息的重要场所&#xff0c;随着城市中的流动人口的增多&#xff0c;人们对房屋租赁需求越来越高&#xff0c;为满足用户查询房屋、预约看房、房屋租赁的需求&#xff0c;特开发了本基于Spring Boot的房屋租赁系统。 本文重点阐述了房屋租赁系统的开发过程&…

【Qt】显示类控件:QLabel、QLCDNumber、QProgressBar、QCalendarWidget

目录 QLabel QFrame 例子&#xff1a; textFormat pixmap、scaledContents alignment wordWrap、indent、margin buddy QLCDNumber 例子&#xff1a; QTimer QProgressBar 例子&#xff1a; QCalendarWidget 例子&#xff1a; QLabel 标签控件&#xff0c;用来显示…

基于STM32的自学习智能小车设计

目录 引言系统设计 硬件设计软件设计系统功能模块 传感器模块控制模块自学习算法模块系统实现 硬件实现软件实现测试与优化结论与展望 1. 引言 随着人工智能和机器学习技术的不断发展&#xff0c;越来越多的智能小车开始实现自主学习与行为决策。传统的智能小车通常依靠固定的…

Android OpenGLES2.0开发(九):图片滤镜

“当你改变想法的时候&#xff0c;记得也要改变你的世界。”——诺曼文森特皮尔 Android OpenGLES开发&#xff1a;EGL环境搭建Android OpenGLES2.0开发&#xff08;一&#xff09;&#xff1a;艰难的开始Android OpenGLES2.0开发&#xff08;二&#xff09;&#xff1a;环境搭…

梳理你的思路(从OOP到架构设计)_简介设计模式

目录 1、 模式(Pattern) 是较大的结构​编辑 2、 结构形式愈大 通用性愈小​编辑 3、 从EIT造形 组合出设计模式 1、 模式(Pattern) 是较大的结构 组合与创新 達芬奇說&#xff1a;簡單是複雜的終極形式 (Simplicity is the ultimate form of sophistication) —Leonardo d…

vscode的keil assistant 中搜索不到全局变量

搜不到 但是在包含的文件中输入 ../../../,就是全局搜索的结果 我的文件结构是&#xff1a;\Desktop\LVGL文件系统移植&#xff08;lvgl8&#xff0e;&#xff13;&#xff09;\Projects\MDK-ARM 盲猜是keil assistant 当前文件夹打开的时候是进入到了MDK-ARM文件夹层次&…

HTML语法规范

HTML语法规则 HTML 标签是由尖括号包围的关键词&#xff0c;标签通常是成对出现的&#xff0c;例如 <html> 和 </html>&#xff0c;称为双标签 。标签对中的第一个标签是开始标签&#xff0c;第二个标签是结束标签单标签比较少&#xff0c;例如<br />&#x…

flink实现复杂kafka数据读取

接上文&#xff1a;一文说清flink从编码到部署上线 环境说明&#xff1a;MySQL&#xff1a;5.7&#xff1b;flink&#xff1a;1.14.0&#xff1b;hadoop&#xff1a;3.0.0&#xff1b;操作系统&#xff1a;CentOS 7.6&#xff1b;JDK&#xff1a;1.8.0_401。 常见的文章中&…