JavaScript异步编程——01-单线程和异步任务

单线程

JS 是单线程的

JavaScript 语言的执行是单线程的。即同一时间,只能处理一个任务。

具体来说,所谓单线程,是指 JS 引擎中负责解释和执行 JavaScript 代码的线程只有一个,即同一时间,只能处理一个任务。这个任务执行完后才能执行下一个。所有的任务都需要排队

JS 为何要被设计为单线程呢?原因如下:

  • 首先是历史原因,在最初设计 JS 这门语言时,多进程、多线程的架构并不流行,硬件支持并不好。

  • 其次是因为多线程的复杂性,多线程操作需要加锁,编码的复杂性会增高。

  • 而且,如果多个线程同时操作同一个 DOM,在多线程不加锁的情况下,会产生冲突,最终会导致 DOM 渲染的结果不符预期。

所以,为了避免这些复杂问题的出现,JS 被设计成了单线程语言。

浏览器是多进程、多线程的

JS代码在执行时有它的运行环境(也称之为“容器”),这个运行环境可以是浏览器,也可以是 Node.js 环境。

浏览器是多进程的,每打开一个新的 tab 标签页就会开启一个新的进程。每个进程之间是独立的,这是为了防止一个页面卡死而造成所有页面都无法响应,甚至整个浏览器强制退出。

每个进程中有很多个线程,其中有一个专门执行JS代码的线程,所以我们常说JS是单线程的,这没有说错。从JS语言的角度看,我们把这个线程称为“主线程”。

如果JS正在执行某个耗时的任务,则当前的线程会被阻塞,那应该怎么办呢?

实际上,耗时的任务并不是在主线程中执行的。因为浏览器的当前进程中有很多个线程,我们可以把耗时任务交给浏览器的其它线程来协助处理,然后在特定的时机通知主线程,该任务则会进入主线程同步完成。

比如,现在有一个三秒延迟的定时器任务。计时工作是交给浏览器的其他线程完成的,等三秒时间到了之后,通知JS主线程,该任务进入主线程进行同步执行。

同步任务和异步任务

定义

当前正在执行的任务,如果没有执行完成,它可能会阻塞其他正在排队的任务。为了解决这个问题,JS 在设计之初,将任务分成了两类:同步任务、异步任务。

  • 同步任务:在主线程上排队执行的任务。只有当前任务执行完毕,才能执行下一个任务。当前任务在没有得到结果之前,不会继续后续操作。

  • 异步任务:不进入主线程、而是进入任务队列(Event Queue)的任务,该任务无论有没有得到结果,都不会阻塞后续任务的执行。只有"任务队列"通知主线程,某个异步任务可以执行了,该任务才会进入主线程执行。

代码举例:

 console.log('同步任务1');​setTimeout(() => {console.log('异步任务');}, 1000);​console.log('同步任务2');

打印结果是:

 同步任务1同步任务2异步任务

代码解释:第一行代码是同步任务,会立即执行;定时器里的回调函数是异步任务,需要等 1 秒后才会执行。假如定时器里的代码是同步任务,那需要等待1秒后,才能执行最后一行代码console.log('同步任务2'),也就是造成了主线程里的同步任务阻塞,这不是我们希望看到的。

比如说,网络图片的请求,就是一个异步任务。前端如果同时请求多张网络网络图片,谁先请求完成就让谁先显示出来。假如网络图片的请求做成同步任务,那就会出大问题,所有图片都得排队加载,如果第一张图片未加载完成,就得卡在那里,造成阻塞,导致其他图片都加载不出来。页面看上去也会很卡顿,这肯定是不能接受的。

前端使用异步编程的场景

什么时候需要等待,就什么时候用异步。常见的异步场景如下:

  • 1、事件监听(比如说,按钮绑定点击事件之后,用户爱点不点。我们不可能卡在按钮那里,什么都不做。所以,应该用异步)

  • 2、回调函数:

    • 2.1、定时器:setTimeout(定时炸弹)、setInterval(循环执行)

    • 2.2、ajax请求。

    • 2.3、Node.js:FS文件读写、数据库操作。

  • 3、ES6 中的 Promise、Generator、async/await

现在的大部分软件项目,都是前后端分离的。后端生成接口,前端请求接口。前端发送 ajax 请求,向后端请求数据,然后等待一段时间后,才能拿到数据。这个请求过程就是异步任务。

接口调用的方式

js 中常见的接口调用方式,有以下几种:

  • 原生 ajax、基于 jQuery 的 ajax

  • Promise

  • Fetch

  • axios

后续文章,我们会重点讲一下接口调用里的 Ajax,然后在 ES6 语法中学习 Promise。在这之前,我们需要先了解同步任务、异步任务的事件循环机制。

多次异步调用的顺序

  • 多次异步调用的结果,顺序可能不同步。

  • 异步调用的结果如果存在依赖,则需要通过回调函数进行嵌套。

定时器:代码示例

掌握了上面的事件循环原理之后,我们来看几个例子。

举例 1

 console.log(1);​setTimeout(() => {console.log(2);}, 1000);console.log(3);console.log(4);

打印结果:

 1342

解释:先等同步任务执行完成后,再执行异步任务。

举例 2(重要)

如果我把上面的等待时间,从 1 秒改成 0 秒,你看看打印结果会是什么。

 console.log(1);​setTimeout(() => {console.log(2);}, 0);console.log(3);console.log(4);

打印结果:

 1342

可以看到,打印结果没有任何变化,这个题目在面试中经常出现,考的就是 setTimeout(()=> {}, 0)会在什么时候执行。这就需要我们了解同步任务、异步任务的执行顺序,即前面讲到的事件循环机制

解释:先等同步任务执行完成后,再执行异步任务。

同理,我们再来看看下面这段伪代码:

 setTimeout(() => {console.log('异步任务');}, 2000);​// 伪代码sleep(5000); //表示很耗时的同步任务

上面的代码中,异步任务不是 2 秒之后执行,而是等耗时的同步任务执行完毕之后,才执行。那这个异步任务,是在 5 秒后执行?还是在 7 秒后执行?这个作业,留给读者你来思考~

举例 3(较真系列)

 setTimeout(() => {console.log('异步任务');}, 1000);

上面的代码中,等到 1 秒之后,真的会执行异步任务吗?其实不是。

在浏览器中, setTimeout()/ setInterval() 的每调用一次定时器的最小时间间隔是4毫秒,这通常是由于函数嵌套导致(嵌套层级达到一定深度),或者是由于已经执行的 setInterval 的回调函数阻塞导致的。

上面的案例中,异步任务需要等待 1004 毫秒之后,才会从 Event Table 进入到 Event Queue。这在面试中也经常被问到。

异步任务举例

例 1:加载图片

// 加载图片的异步任务
function loadImage(file, success, fail) {const img = new Image();img.src = file;img.onload = () => {// 图片加载成功success(img);};img.onerror = () => {// 图片加载失败fail(new Error('img load fail'));};
}loadImage('images/qia nguyihao.png',(img) => {console.log('图片加载成功');document.body.appendChild(img);img.style.border = 'solid 2px red';},(error) => {console.log('图片加载失败');console.log(error);}
);

例 2:定时器计时,移动 DOM 元素

// 函数封装:定义一个定时器,每间隔 delay 毫秒之后,执行 callback 函数
function myInterval(callback, delay = 100) {let timeId = setInterval(() => callback(timeId), delay);
}myInterval((timeId) => {// 每间隔 500毫秒之后,向右移动 .box 元素const myBox = document.getElementsByClassName('box')[0];const left = parseInt(window.getComputedStyle(myBox).left);myBox.style.left = left + 20 + 'px';if (left > 300) {clearInterval(timeId);// 每间隔 10 毫秒之后,将 .box 元素的宽度逐渐缩小,直到消失myInterval((timeId2) => {const width = parseInt(window.getComputedStyle(myBox).width);myBox.style.width = width - 1 + 'px';if (width <= 0) clearInterval(timeId2);}, 10);}
}, 200);

参考链接

  • JS-同步任务,异步任务,微任务,和宏任务

  • JS 同步异步宏任务微任务、JavaScript 中事件循环的理解、javascript 事件循环机制

  • 如何实现比 setTimeout 快 80 倍的定时器?

希望各位可以点个赞点个关注,这对up真的很重要,谢谢大家啦!

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

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

相关文章

CellMarker | 人骨骼肌组织细胞Marker大全!~(强烈建议火速收藏!)

1写在前面 分享一下最近看到的2篇paper关于骨骼肌组织的细胞Marker&#xff0c;绝对的Atlas级好东西。&#x1f44d; 希望做单细胞的小伙伴觉得有用哦。&#x1f60f; 2常用marker&#xff08;一&#xff09; general_mrkrs <- c( MYH7, TNNT1, TNNT3, MYH1, MYH2, "C…

ComfyUI 基础教程(十三):ComfyUI-Impact-Pack 面部修复

SD的WebUI 中的面部修复神器 ADetailer,无法在ComfyUI 中使用。那么如何在ComfyUI中进行面部处理呢?ComfyUI 中也有几个面部修复功能,比如ComfyUI Impact Pack(FaceDetailer),以及换脸插件Reactor和IPAdapter。 ComfyUI-Impact-Pack 是一个功能强大的插件,专为 ComfyUI …

GDB的常用命令

GDB&#xff08;GNU Debugger&#xff09;是Linux和其他类Unix系统中常用的调试工具&#xff0c;用于调试C、C等编程语言编写的程序。以下是一些GDB常用的命令&#xff1a; 启动和基本操作 gdb [executable-file]&#xff1a;启动GDB并加载指定的可执行文件。 run [arguments]…

淘宝扭蛋机小程序开发:开启你的惊喜之旅

一、扭出新世界&#xff0c;惊喜不断 在这个充满无限可能的数字时代&#xff0c;淘宝扭蛋机小程序为你带来了一种全新的购物与娱乐体验。扭蛋机&#xff0c;这个充满童趣和惊喜的玩具&#xff0c;如今在我们的小程序中焕发出新的活力&#xff0c;为你带来一波又一波的惊喜与快…

【CTF Web】XCTF GFSJ0485 simple_php Writeup(代码审计+GET请求+PHP弱类型漏洞)

simple_php 小宁听说php是最好的语言,于是她简单学习之后写了几行php代码。 解法 &#xfeff;<?php show_source(__FILE__); include("config.php"); $a$_GET[a]; $b$_GET[b]; if($a0 and $a){echo $flag1; } if(is_numeric($b)){exit(); } if($b>1234){ech…

jquery项目 html使用export import方式调用模块

jquery的老项目&#xff0c;引入vue3, 需要方便使用export, import方式引用一些常用的方法与常量 导出模块 export js/numberUtil.js /*** Description:* Author Lani* date 2024/1/10*//* * 【金额】 保留2位小数&#xff0c;不四舍五入 * 5.992550 >5.99 , 2 > 2.…

【Kaggle】练习赛《洪水数据集的回归预测》(下)

前言 上篇《洪水数据集的回归预测》(上) 介绍了该数据集非常特殊之处&#xff0c;各特征都非常类似&#xff0c;没有特别之处&#xff0c;各特征之间的相关系数几乎为零。同时&#xff0c;各类模型不敏感&#xff0c;最理想的模型居然是线性回归&#xff0c;决定系数 R 2 R^2 …

64位Office API声明语句第118讲

跟我学VBA&#xff0c;我这里专注VBA, 授人以渔。我98年开始&#xff0c;从源码接触VBA已经20余年了&#xff0c;随着年龄的增长&#xff0c;越来越觉得有必要把这项技能传递给需要这项技术的职场人员。希望职场和数据打交道的朋友&#xff0c;都来学习VBA,利用VBA,起码可以提高…

Python进阶:函数

Python进阶 函数的定义和调用 函数定义通常采用def关键词,后面跟着函数名及圆括号内的参数列表。 def function_name (parameters):"""函数文档字符串"""# 函数体...return expression # 可选&#xff0c;用于返回函数结果function_name是函数…

富格林:曝光欺诈陷阱阻挠受骗亏损

富格林悉知&#xff0c;今年以来&#xff0c;国际金融市场动荡&#xff0c;黄金价格则表现强势&#xff0c;黄金投资热潮再起&#xff0c;黄金作为避险工具的价值再度成为世人瞩目的焦点。然而做好黄金投资却不是那么容易的&#xff0c;没有掌握一定的做单技巧&#xff0c;便难…

达梦数据库导入数据问题

进行数据导入的时候遇到了导入数据问题 第一个问题&#xff1a; 该工具不能解析此文件&#xff0c;请使用更高版本的工具 这个是因为版本有点低&#xff0c;需要下载最新的达梦数据库 第二个问题&#xff1a; &#xff08;1&#xff09;本地编码&#xff1a;PG_GBK, 导入文…

全国31省对外开放程度、经济发展水平、ZF干预程度指标数据(2000-2022年)

01、数据介绍 自2000年至2022年&#xff0c;中国的对外开放程度不断深化、经济发展水平不断提高、ZF不断探索并调整自身在经济运行中的角色和定位&#xff0c;以更好地适应国内外环境的变化&#xff0c;也取得了举世瞩目的成就。这一期间&#xff0c;中国积极融入全球经济体系…

《1w实盘and大盘基金预测 day30》

今日预测&#xff1a; 3123-3150-3177 探底回升&#xff0c;震荡上涨&#xff0c;收小红小绿 双创指数后期上涨的幅度也是会大于上证的&#xff0c;四月底的时候就提醒建仓。 关注板块&#xff1a;医疗、地产、电力、证券 这周预测 这周上证指数最高看到3200 继续看涨&#…

简单的知识蒸馏

import os os.environ[TF_CPP_MIN_LOG_LEVEL] 2 os.environ["KERAS_BACKEND"] "tensorflow" os.environ[HF_ENDPOINT] https://hf-mirror.com import keras from keras import layers from keras import ops import numpy as np # 随着训练的进行&…

大模型日报2024-05-06

大模型日报 2024-05-06 大模型技术 智谱AI 正研发对标Sora的国产文生视频模型&#xff0c;最快年内发布 摘要: 估值超200亿的国内 A1大模型独角兽公司“智谱 A“正在研发对标OpenAl Sora的高质量文生视频模型&#xff0c;预计最快年内发布。据悉&#xff0c;成立于2019年的智谱…

selenium解放双手--记某电力学校的刷课脚本

免责声明:本文仅做技术交流与学习... 重难点: 1-对目标网站的html框架具有很好的了解,定位元素,精准打击. 2-自动化过程中窗口操作的转换. 前置知识: python--selenium模块的操作使用 前端的html代码 验证码自动化操作 Chrome & Chromedriver : Chrome for Testing ava…

[机器学习-02] 数据可视化神器:Matplotlib和Seaborn工具包实战图形大全

目录 引言 正文 01-Matplotlib包的使用示例 1&#xff09;Matplotlib导入方式 2&#xff09;折线图绘制 3&#xff09;散点图绘制 4&#xff09;柱状图绘制 5&#xff09;饼图绘制 6&#xff09;等高线图绘制 7&#xff09;箱线图绘制 8&#xff09;较为复杂…

7zip如何只压缩文件不带上级目录?

在使用7zip进行文件压缩的时候&#xff0c;如果直接选择要压缩的文件进行压缩&#xff0c;得到的压缩包则会多包含一层顶层目录&#xff0c;解压缩之后需要点击两次才能进入到实际目录中&#xff0c;为了解决这个问题&#xff0c;本文根据探索找到了一种解决办法。 如下是一个演…

表空间的概述

目录 表空间的属性 表空间的类型 永久性表空间(PermanentTablespace) 临时表空间(Temp Tablespace ) 撤销表空间(Undo Tablespace) 大文件表空间(BigfileTablespace) 表空间的状态 联机状态(Online) 读写状态(Read Write) 只读状态(Read) 脱机状态(Offline) Oracle从…