网络请求 - 异步编程详解

一、概述

网络管理模块主要提供以下功能:

  • HTTP数据请求:通过HTTP发起一个数据请求。
  • WebSocket连接:使用WebSocket建立服务器与客户端的双向连接。
  • Socket连接:通过Socket进行数据传输。

HTTP和WebSocket都是啥?

        比如我们去逛某宝的商品列表,从HTTP协议的角度来看,前端发送了一次HTTP请求,网站返回一次HTTP响应。不过从始至终服务器都不会主动给客户端发送消息请求(就像你喜欢的人从来不会主动找你一样),这就是HTTP协议的特点。

        又比如我们玩传奇一刀999的网页游戏,我们甚至全程都没有点一次鼠标,但是服务器就源源不断地将怪物的移动数据和攻击数据发给我们。这种服务器可以主动给客户端发送消息的可双向传输数据场景就是使用了WebSocket协议。

        至于Socket协议,套接字(socket)是一个抽象层,应用程序可以通过它发送或接收数据,可对其进行像对文件一样的打开、读写和关闭等操作。套接字允许应用程序将I/O插入到网络中,并与网络中的其他应用程序进行通信。网络套接字是IP地址与端口的组合。它偏向于底层,与WebSocket的关系就如Java和JavaScript,可以说是除了名字相似之外没什么关系。


二、异步Promise

1、并发、并行、同步和异步

并发

并发是一个比较宽泛的概念,它单纯代表计算机可以执行多项任务

比如一个单核处理器,计算机可以通过分配时间片的方式,让一个任务运行一段时间后切换到另一个任务。不同的任务就这样交替反复的一直执行下去。这个过程也被称作是进程或者线程的上下文切换

并行 

对于多核处理器,计算机可以在不同的核心上真正的并行执行任务,而不用像分配时间片这样的伪同时执行任务

 

同步&异步

至于同步和异步,它们则是两种不同的编程模型

同步代表需要等到前一个任务完成之后才可以执行下一个任务,因此在同步中并没有并发或者并行的概念

异步则代表不同的任务之间并不会相互等待先后执行,也就是说执行任务A时也可以执行任务B

一个典型的实现异步的方式就是多线程编程(如Java)

但是对于某些编程语言如JavaScript它们本身是没有多线程概念的,不过通过它的函数回调(function callback)机制,依旧可以做到单线程的并发

比如在这个案例中,fetch函数用来获取资源,他并不是按照函数编写的顺序去等待addImage()方法执行完毕再输出console.log(),而是跳过了该方法继续执行下面的代码。当获取到资源之后回调函数才被执行。

 

 注意:再次强调,JavaScript从设计之初就是一个单线程的语言,这样虽然看起来是同时运行的,但其底层还是运行在一个线程上运行的并发。

不过虽然只有单个线程在执行,不过这种单线程的异步编程还是有不少优点的

 所有的操作都运行在同一个线程中,你无需考虑线程同步或资源竞争的问题,从源头上避免了多线程之间的频繁切换,降低了线程自身的开销。

2、回调函数

在JavaScript中有两种实现异步的方式,第一种就是回调函数

什么是回调函数?其实很简单,就是一个函数被当成参数被另一个函数调用而已

比如:

其缺点也很明显,比如setTimeout是js自带的一个定时器,可以在其中传入一个回调函数

假如我现在想要在回调函数中继续调用回调函数,就会变成这样 

 这种回调函数嵌套的代码使得整段代码变得极其的诡异和难看,这就叫回调地狱(Callback Hell)

为了解决这个问题Promise应运而生

3、Promise

Promise也是第二种实现异步的方式

在JavaScript中,Promise对象是用来处理异步操作的一种机制,它表示一个异步操作的最终完成或失败,并且可以链式地处理这些操作。Promise对象有三个状态:pending(进行中)、fulfilled(已成功)和rejected(已失败)。

let myPromise = new Promise((resolve, reject) => {
  // 异步操作,可以是Ajax请求、文件读取等
  // 如果操作成功,调用 resolve(value)
  // 如果操作失败,调用 reject(reason)
});

myPromise.then((result) => {
  // 处理操作成功的情况
}).catch((error) => {
  // 处理操作失败的情况
});

在创建Promise对象时,你会得到一个具有reslove和reject两个参数的函数。这两个参数都是函数,用于改变Promise对象的状态。具体来说:

  • resolve函数用于将Promise对象从pending(进行中)变为fulfilled(已成功)。

通常,当异步操作成功完成时,就调用该函数,结束异步请求并传递操作的结果作为参数

const myPromise = new Promise((resolve, reject) => {
  // 异步操作成功
  resolve("操作成功");
});
  • reject函数用于将Promise对象的状态从pending(进行中)变为rejected(已失败)。

当异步操作发生错误或失败时,你会调用该函数,结束异步请求并传递错误信息作为参数

const myPromise = new Promise((resolve, reject) => {
  // 异步操作失败
  reject("发生错误");
});

所以也就是说,如果不在异步调用中使用这两个函数的话,当你在别的地方调用这个异步请求时它会一直处于进行中状态,这两个异步请求自然也是是没有返回值的,因此也不会执行后面的回调逻辑。

这里再次强调一遍:Promise本身是无法中止的,其本身只是一个状态机,存储三个状态(pending,resolved,rejected),一旦发出请求了,必须闭环,无法取消。如果你不使用resolve/reject函数那么这个 Promise 将永远保持在 pending 状态。这样的 Promise 并不会自动转变为 fulfilled 或 rejected。

 4、async和await

简单来说,async和await是基于Promise的一个语法糖,可以让异步操作更加简单明了。所以也就是说在理论上,async/await 语法与 Promise 的链式写法在性能上没有本质的区别。它们都基于 JavaScript 的异步执行模型,实际性能差异较小。

不过好处当然是有的,不然还用它干什么

简而言之,使用async和await可以替换Promise的链式写法,让异步代码写的看起来像同步代码一样;替换掉.then,而.catch就用try...catch取代掉

不同于Promise的链式写法,写在async/await中想要中断程序就很简单了,因为语义化非常明显,其实就和一般的function写法一样,想要中断的时候,直接return一个值就行,null,空,false都是可以的。

比如这段Promise的链式操作的TypeScript代码

使用了async与await语法糖之后就变成了

是不是感觉就像以前写的同步代码一样,一下就顺眼起来了

那它是怎么实现的呢?

他们俩是一对好夫妻,缺一不可,async必须声明的是一个function,如果声明别的await就会很生气觉得它是渣男,就不生效了(报错)

async声明的函数无论如何都会返回一个Promise,比如

这是因为async将它自动解析成了Promise.resolve(),也就是说,相当于这段代码的效果

await是可以提供等同于"同步效果"的等待异步返回能力的语法糖

顾名思义,就是等一会。等什么?等一个Promise的异步返回,只要await声明的函数还没有返回数据,那么下面的程序是不会去执行的当这个Promise有了返回值,await的结果才会被取得,程序才会接着向下运行。

不过有人常说有了async/await的出现淘汰了Promise,这是错误的。两者是相辅相成的,缺一不可,你可以同时使用这两种语法,或者你更喜欢哪种语法就使用哪种语法

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

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

相关文章

速记计算机网络传输介质分类

计算机网络中的传输介质是指用于在不同设备之间传递数据的物理媒介。传输介质的选择对于网络的性能和可靠性至关重要。在计算机网络中,常见的传输介质可以分为有线传输介质和无线传输介质两类。本文将对这两类传输介质进行详细的分类和介绍。 一、有线传输介质 有…

外包干了4个月,技术退步明显了...

先说一下自己的情况,大专生,18年通过校招进入武汉某软件公司,干了接近4年的功能测试,今年年初,感觉自己不能够在这样下去了,长时间呆在一个舒适的环境会让一个人堕落! 而我已经在一个企业干了四…

[C]jupyter中使用C

[C]jupyter中使用C 安装使用用处 安装 https://github.com/brendan-rius/jupyter-c-kernel 下拉找到3条命令,装就可以了 mac和linux可用 python3可用, 2不可以 第二条命令可以改为 : python3 install_c_kernel 小总结:如果有问题&#xff0…

Merge还是Rebase?这次终于懂了

《Git分支管理:Merge还是Rebase?》 导语: 在Git的分支管理中,Merge和Rebase是两种常见的合并策略,每一种都有其优劣之处。究竟应该选择Merge还是Rebase,取决于项目的需求和团队的工作流程。本文将深入探讨…

echarts 仪表盘进度条 相关配置

option {series: [{type: gauge,min: 0,//最大值max: 100, //最小值startAngle: 200,//仪表盘起始角度。圆心 正右手侧为0度,正上方为90度,正左手侧为180度。endAngle: -20,//仪表盘结束角度splitNumber: 100, //仪表盘刻度的分割段数itemStyle: {color…

WPF 使用矢量字体图标

矢量字体图标 在WPF项目中经常需要显示图标,但是项目改动后,有时候需要替换和修改图标,这样非常麻烦且消耗开发和美工的时间。为了快速开发项目,节省项目时间,使用图标矢量字体图标是一个非常不错的选择。 矢量字体图标…

【java爬虫】首页显示沪深300指数走势图以及前后端整合部署方法

添加首页 本文我们将在首页添加沪深300指数成立以来的整体走势数据展示,最后的效果是这样的 单独贴一张沪深300整体走势图 我感觉从总体上来看指数还是比较稳的,没有特别大的波动,当然,这只是相对而言哈哈。 首先是前端页面 &l…

C++性能优化- perf 和火焰图的安装使用

工欲善其事必先利其器,要想做Linux下的程序性能优化,就得先知道当前性能的瓶颈在哪里。 这里主要介绍一下常用的工具:perf工具和火焰图的使用方法 本文中的命令都是自己在Ubuntu18.04系统上测试可用的,在其他系统可能会需要不同的…

数据库的连接

连接数据库 我们使用WinR输入cmd打开运行窗口 输入:sqlplus并回车 输入用户名和密码,我用的是Scott,密码我自己设置的123456,Scott默认的密码是tiger,回车 这种情况表示登录成功 在连接Scott成功的情况下创建一些数据,在我的资源里面有个Oracle数据基础可以下载,直接复制粘…

HarmonyOS 开发基础(六)Slider

HarmonyOS 开发基础(六)Slider Entry Component struct Index {build() {Row() {Column() {// Slider:ArkUI 的基础组件 滑动条组件// options 参数:Slider 基础设置Slider({// 最小值min: 20,// 最大值max: 200,// 当前值value: …

使用Spring Retry优雅的实现业务异常重试

在系统中经常遇到业务重试的逻辑,比如三方接口调用,timeout重试三遍,异常处理重试的兜底逻辑等。那你是不是还在用下面这种方式呢: 我想大家可能很多时候也会这么写,这是能想到的第一个方法,但是我们这段代…

AP2813 双路降压恒流驱动IC 一路内置1A一路外置3A LED储能指示灯线路

产品描述 AP2813 是一款双路降压恒流驱动器,高效率、简单、内置功率管,适用于 5-80V 输入的高精度降 压 LED 恒流驱动芯片。内置功率管输出功率可达 12W,电流 1.2A。 AP2813 一路直亮,另外一路通过 MODE1 切换 全亮,爆闪。AP2813…

嵌入式(四)定时器 | 定时器功能 分类 定时器工作模式 寄存器全介绍

文章目录 1 定时器工作原理2 定时器功能3 定时器分类3.1 定时器13.2 定时器23.3 定时器3和定时器43.4 睡眠定时器3.5 看门狗定时器 4 定时器工作模式4.1 自由运行模式4.2 模模式4.3 正计数/倒计数模式 5 定时器1寄存器5.1 计数寄存器5.2 计数控制寄存器 6 定时器的两种使用方式…

junit单元测试:使用@ParameterizedTest 和 @CsvSource注解简化单元测试方法

在平常的开发工作中,我们经常需要写单元测试。比如,我们有一个校验接口,可能会返回多种错误信息。我们可以针对这个接口,写多个单元测试方法,然后将其场景覆盖全。那么,怎么才能写一个测试方法,…

实验笔记之——基于Linux服务器复现Instant-NGP及常用的tmux指令

之前博客实现了基于windows来复现Instant-NGP,本博文在linux服务器上测试 实验笔记之——基于windows复现Instant-NGP-CSDN博客文章浏览阅读444次,点赞15次,收藏7次。之前博客对NeRF-SLAM进行了调研,本博文先复现一下Intant-NGP。…

C++开发小技巧

C开发一些小技巧 积累一些能用得到的C开发小技巧。 错误码/状态码 错误码/状态码在项目很常见,用于提示错误类型、状态,通常还会附带一些相关描述。通常错误码是统一管理的,例如使用宏或者枚举定义。 平时我的做法 使用宏或者枚举定义错…

rust 注释文档生成 cargo doc

rust的cargo文档生成 只需要在每个函数写清楚注释,就可以自动生成文档,很方便 即不用写文档,又可以快速查看,是开发rust的必备技能 rust安装和开发环境配置,可以参考:链接 1.写注释的方法 连续三个 \ 即…

springboot + vue3实现增删改查分页操作

springboot vue3实现增删改查分页操作 环境最终实现效果实现功能主要框架代码实现数据库后端前端 注意事项 环境 jdk17 vue3 最终实现效果 实现功能 添加用户,禁用,启用,删除,编辑,分页查询 主要框架 后端 spri…

在vue3中使用Cesium保姆篇

1.首先新建一个vue项目 Vue.js - 渐进式 JavaScript 框架 | Vue.js 可以直接到管网中查看命令通过npm来创建一个vue3的项目 然后通过命令下载1.99的版本的cesium和plugin npm i cesium1.99 vite-plugin-cesium 下载完了以后 2.引入cesium 首先找到vue的vite.config.js …

技术资讯:谷歌最新发布!2023年度最受欢迎的Chrome扩展榜单

大家好,我是大澈! 本文约1300字,整篇阅读大约需要2分钟。 感谢关注微信公众号:“程序员大澈”,免费领取"面试礼包"一份,然后免费加入问答群,从此让解决问题的你不再孤单&#xff01…