前端一面之 同步 vs 异步

news2024/9/20 1:29:21

异步 vs 同步

先看一下 下面的 demo

console.log(100)
setTimeout(function () {
    console.log(200)
}, 1000)
console.log(300) 

执行结果

100
300
200
console.log(100)
alert(200)  // 1秒钟之后点击确认
console.log(300) 

这俩到底有何区别?——
第一个示例中间的步骤根本没有阻塞接下来程序的运行,
而第二个示例却阻塞了后面程序的运行。
前面这种表现就叫做 异步(后面这个叫做 同步 ),
即不会阻塞后面程序的运行。

异步和单线程

JS 需要异步的根本原因是 JS 是单线程运行的,即在同一时间只能做一件事,不能“一心二用”。

一个 Ajax 请求由于网络比较慢,请求需要 5 秒钟。
如果是同步,这 5 秒钟页面就卡死在这里啥也干不了了。
异步的话,就好很多了,5 秒等待就等待了,其他事情不耽误做,
至于那 5 秒钟等待是网速太慢,不是因为 JS 的原因。

讲到单线程,我们再来看个真题:

讲解一下下面代码的执行结果

var a = true;
setTimeout(function(){
    a = false;
}, 100)
while(a){
    console.log('while执行了')
} 

这是一个很有迷惑性的题目,不少候选人认为100ms之后,
由于a变成了false,所以while就中止了,
实际不是这样,因为JS是单线程的,
所以进入while循环之后,
没有「时间」(线程)去跑定时器了,
所以这个代码跑起来是个死循环!

异步任务

异步任务是在主线程执行的同时,
通过回调函数或其他机制委托给其他线程或事件来处理的任务。
在执行异步任务时,主线程不会等待任务完成,
而是继续执行后续代码。包括:
在这里插入图片描述

console.log('Start');

setTimeout(() => {
  console.log('Timeout callback');
}, 1000);

console.log('End');

在上述例子中,setTimeout 是一个异步任务,
它会在1秒后将回调函数推入任务队列
而主线程不会等待这个1秒,
而是继续执行后面的 console.log(‘End’)。
当主线程的同步任务执行完成后
它会检查任务队列,
将异步任务的回调函数推入执行栈,最终输出 ‘Timeout callback’。

任务队列类型

任务队列分为宏任务队列(macrotask queue)微任务队列(microtask queue)两种。
JavaScript 引擎遵循事件循环的机制,
在执行完当前
宏任务
后,
会检查微任务队列,执行其中的微任务,
然后再取下一个宏任务执行。
这个过程不断循环,形成事件循环。

  1. 宏任务队列

所有同步任务
I/O 操作, 文件读写 数据库读写等等
setTimeout、setInterval
setImmediate(Node.js环境)
requestAnimationFrame
事件监听回调函数

  1. 微任务(Microtasks)是一些较小粒度、高优先级的任务,包括:

Promise的then、catch、finally
async/await中的代码
Generator函数
MutationObserver
process.nextTick(Node.js 环境)

任务执行过程

首先,必须要明确,在JavaScript中,所有任务都在主线程上执行
任务执行过程分为同步任务和异步任务两个阶段
异步任务的处理经历两个主要阶段
Event Table(事件表)和 Event Queue(事件队列)。
Event Table存储了宏任务的相关信息
包括事件监听和相应的回调函数。
当特定类型的事件发生时,对应的回调函数被添加到事件队列中,等待执行。
例如,你可以通过addEventListener来将事件监听器注册到事件表上:

document.addEventListener('click', function() {
  console.log('Hello world!');
});

微任务与 Event Queue 密切相关。
当执行栈中的代码执行完毕后,JavaScript引擎会不断地检查事件队列
如果队列不为空就将队列中的事件一个个取出,并执行相应的回调函数。

任务队列的执行流程可概括为:
同步任务在主线程排队执行,异步任务在事件队列排队等待进入主线程执行。
遇到宏任务则推进宏任务队列,遇到微任务则推进微任务队列。
执行宏任务,执行完毕后检查当前层的微任务并执行。
继续执行下一个宏任务,执行对应层次的微任务,直至全部执行完毕。

console.log(1);

setTimeout(() => {
    console.log(2);
}, 0);

console.log(3);

new Promise((resolve) => {
    console.log(4);
    resolve();
    console.log(5);
}).then(() => {
    console.log(6);
});

console.log(7);

执行顺序解析:1 => 3 => 4 => 5 => 7 => 6 => 2。

  1. 创建Promise实例是同步的,所以1、3、4、5、7是同步执行的。
  2. then方法是微任务,放入微任务队列中,在当前脚本执行完毕后立即发生。
  3. 同步任务执行完毕后,执行微任务队列中的微任务。
  4. 最后,setTimeout放入宏任务队列,按照先进先出的原则执行。

拓展
以下是一个使用同步I/O操作的例子,这种操作会阻塞整个程序的执行

const fs = require('fs');

function readFileSync() {
    // 打印"Start"到控制台
    console.log('Start');

    // 同步读取文件的内容
    // 在文件读取完成之前,这行代码会阻塞事件循环,其他任务无法执行
    const data = fs.readFileSync('/opt/xiaodou.txt', 'utf8');
    // 打印读取到的文件内容到控制台
    console.log('File data:', data);
    // 打印"End"到控制台
    console.log('End');
}

readFileSync();

在这个例子中,fs.readFileSync是一个同步的文件读取操作,它会阻塞事件循环,直到文件读取完成。在读取文件的过程中,其他任务无法执行,整个程序的执行被阻塞。
这段代码的执行顺序是严格线性的,整个程序会在读取文件时被阻塞,直到读取操作完成。
为了对比,可以看一下使用异步I/O操作的代码,这样的代码不会阻塞事件循环:

const fs = require('fs');

function readFileAsync() {
    console.log('Start');

    // 读取文件的异步操作,这不会阻塞事件循环
    fs.readFile('/opt/xiaodou.txt', 'utf8', (err, data) => {
        if (err) {
            console.error('Error reading file:', err);
            return;
        }
        console.log('File data:', data);
    });

    console.log('End');
}

readFileAsync();

在这个例子中,fs.readFile是一个异步的文件读取操作,
它不会阻塞事件循环,程序可以继续执行其他任务。
现在对JavaScript中的同步是不是有一些理解:
导致整个程序的执行被阻塞是同步只是暂停当前函数执行是异步
使用await关键字时,它会暂停当前异步函数的执行,等待异步操作完成,
但不会阻塞事件循环,其他任务可以继续执行。

我们来看一下阻塞整个程序的代码,不仅会阻塞当前函数,还会阻塞整个事件循环,
影响所有其他任务的执行,
咱们现在在上面的同步函数中增加一个定时器,那么你猜猜定时器还能定时执行吗?

const fs = require('fs');

function readFileSync() {
    console.log('Start');

    // 设置一个定时器,计划在1秒后执行
    setTimeout(() => {
        console.log('Timer executed');
    }, 1000);

    // 同步读取文件的操作,这会阻塞事件循环
    const data = fs.readFileSync('/opt/xiaodou.txt', 'utf8');
    console.log('File data:', data);

    console.log('End');
}

readFileSync();

在这个例子中,fs.readFileSync是一个同步操作,
它会阻塞事件循环,假设文件在1秒后无法读取完成,
那么定时器无法在预定的1秒后执行。
只有当文件读取操作完成后,定时器才会有机会执行。
图解
在这里插入图片描述

这是一个简化的时序图,事件循环的主要步骤如下:
开始事件循环:JavaScript引擎开始执行代码。
同步任务队列:引擎首先检查同步任务队列(实际上是调用栈中的任务),执行队列中的任务。
执行同步任务:引擎开始执行同步任务,直到队列为空。
检查微任务队列:同步任务执行完毕后,引擎检查微任务队列。
执行微任务:如果有微任务(如Promise的.then()回调),引擎会执行这些微任务。
检查宏任务队列:微任务执行完毕后,引擎检查宏任务队列。
执行宏任务:如果有宏任务(如setTimeout回调),引擎会执行这些宏任务。执行完毕后再次检查微任务队列。
等待新任务:如果宏任务执行完毕,引擎会等待新的任务(如新的异步操作或宏任务)。
循环:引擎会不断地循环执行上述步骤。
在这里插入图片描述
在这里插入图片描述

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

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

相关文章

5,智能合约(react+区块链实战)

5,智能合约(react区块链实战) 5-1 智能合约5-2 metamask安装及私有链搭建互相联动5-3 solidity数据类型-布尔-数字-地址(owner区别)5-4 solidity 数组和映射(代币转账)5-5 solidity结构体与枚举…

强制升级最新系统,微软全面淘汰Win10和部分11用户

说出来可能不信,距离 Windows 11 正式发布已过去整整三年时间,按理说现在怎么也得人均 Win 11 水平了吧? 然而事实却是,三年时间过去 Win 11 占有率仅仅突破到 29%,也就跳起来摸 Win 10 屁股的程度。 2024 年 6 月 Wi…

【测试文档】系统测试报告(Word原件2024)

软件测试报告在软件开发过程中起着至关重要的作用,主要有以下几个主要原因: 1、确保软件质量 2、提供决策支持 3、记录测试过程和结果 4、促进沟通和协作 5、符合标准和法规要求 6、改进测试流程和策略 7、降低风险 软件开发全套资料获取进主页或者本文末…

【redis缓存】怎么使用 Redis 实现一个延时队列?

redis实现延时队列 1 回答2 代码实现2.1 利用 Redis 过期消息实现延时队列2.1.1 配置键空间通知2.1.2 应用程序订阅过期事件 2.2 使用 Sorted Set 实现延时队列2.2.1 实现思路2.2.2 详细步骤 2.3 Redisson 实现延迟队列2.3.1 添加Redisson依赖2.3.2 任务生产者类 TaskProducer2…

一文入门【NestJs】Providers

Nest学习系列 ✈️一文入门【NestJS】 ✈️一文入门【NestJs】Controllers 控制器 🚩 前言 在NestJS的世界里,理解“Providers”是构建健壮、可维护的后端服务的关键。NestJS,作为Node.js的一个现代框架,采用了Angular的一些核…

轻松搭建系统,让每个故事都精彩绽放!

"轻松搭建系统,让每个故事都精彩绽放!" 这句话传递了一个核心理念,即通过简化、高效的系统搭建过程,让每一个创意故事都能以最佳状态呈现给观众,实现其独特魅力和价值的最大化。 1、模块化设计:系…

Docker初识及使用研究

公司使用docker,小组成员人人都是默默使用,也没讲解培训,真是搞笑。 目录 1. Docker安装 1. Docker安装 需要使用梯子访问docker官网:Install Docker Engine on Ubuntu 此处有个疑惑:Docker Engine 与Docker、Docker…

排序(一)——冒泡排序、直接插入排序、希尔排序(BubbleSOrt,InsertSort,ShellSort)

欢迎来到繁星的CSDN,本期的内容主要包括冒泡排序(BubbleSort),直接插入排序(InsertSort),以及插入排序进阶版希尔排序(ShellSort)。 废话不多说,直接上正题! 一、冒泡排序 冒泡排序…

国漫推荐04

童年经典 1.《喜羊羊与灰太狼》 2.《虹猫蓝兔七侠传》 3.《虹猫蓝兔走天涯》 4.《虹猫蓝兔光明剑》 5.《蓝猫龙骑团》 6.《爆丸小子》 7.《超智能足球》 8.《摩尔庄园》 9.《赛尔号》 10.《恐龙宝贝》 11.《天上掉下个猪八戒》 12.《天眼》

tk 设置文本颜色

import tkinter as tk from tkinter import messageboxdef change_text_color(color):# 获取选中的文本范围start_index text_widget.tag_ranges(tk.SEL)if start_index:# 如果文本被选中,只改变选中文本的颜色tag_name f"color_{color}"# 移除选中文本…

Internet Download Manager V6.43被用15天 怎么激活IDM

互联网下载管理器是一个广泛使用的软件,它可以帮助用户更好地管理和加速他们的下载。最新版本Internet Download Managerv6.43已经发布,它带来了一系列新功能和改进,让用户更加方便和快速地下载他们需要的文件。 新版本Internet Download Ma…

通过手机供网、可修改WIFI_MAC的网络设备

一、修改WIFI mac(bssid) 取一根网线,一头连着设备黄色网口、一头连着电脑按住设备reset按键,插入电源线,观察到蓝灯闪烁后再松开reset按键 打开电脑浏览器,进入192.168.1.1,选择“MAC 地址修改…

书生大模型实战营-入门第2关-python单词计数

python单词计数 统计单词数量 统计单词数量 统计单词数量,拆分为以下几步: 需要剔除文本中的标点符号与换行符大小写转换单词拆分计数 详细代码如下: import redef wordcount(text):"""单词计数标点去除大小写转换"&…

Java版Flink使用指南——背压

大纲 新建工程模拟函数自定义无界流背压测试引入数据低压侧高压侧测试结果 优化降低算法复杂度提高并行度 工程代码 背压(Backpressure)又称“反压”,是指在Flink的处理过程中,某个过程出于某种原因,消耗的上游数据过慢…

分析逆向案例十三——拍拍贷登录密码逆向和JS原型链

网址:拍拍贷 登陆页面,找登陆包,密码和用户名都进行了加密。 直接参数搜索 ,一眼下面的第二条,直接点击进入 加密位置打上断点分析,一个加密嵌套另一个加密。 中间的e.md5好像是md5加密,打印一…

windows系统上python3安装open3d第三方库

打开命令提示符,输入, pip install open3d -i https://pypi.tuna.tsinghua.edu.cn/simple成功页面,

百度安全大模型智能体实践入选信通院“安全守卫者计划”优秀案例

7月3日,由全球数字经济大会组委会主办,中国信息通信研究院(以下简称中国信通院)与中国通信标准化协会联合承办的2024全球数字经济大会“云和软件安全论坛暨第二届SecGo云和软件安全大会”在北京召开。本届论坛聚焦云和软件安全最新…

【Stable Diffusion】(基础篇三)—— 图生图基础

图生图基础 本系列笔记主要参考B站nenly同学的视频教程,传送门:B站第一套系统的AI绘画课!零基础学会Stable Diffusion,这绝对是你看过的最容易上手的AI绘画教程 | SD WebUI 保姆级攻略_哔哩哔哩_bilibili 本文主要讲解如何使用S…

线性系统理论及应用GUI设计及仿真

目录 1.控制系统的状态空间模型 1.1.状态空间模型 1.2 传递函数模型 1.3 传递函数转换为状态空间模型 1.4.状态空间模型转换为传递函数 1.5.状态空间模型转化为约当标准型 2.线性系统的时域分析 2.1.矩阵指数函数的计算 2.2.线型定常连续系统的状态空间模型求解 3.线…

java后端项目启动失败,解决端口被占用问题

报错信息: Web server failed to start . Port 8020 was already in use. 1、查看端口号 netstat -ano | findstr 端口号 2、终止进程 taskkill /F /PID 进程ID 举例:关闭8020端口