【前端】浅谈async/await异步传染性

文章目录

  • 概述
  • 观点
    • 无法解决
    • 可以解决
  • 来源

概述

"异步传染性"问题通常是指,当一个函数使用了async和await,其调用者也需要使用async和await处理异步操作,导致整个调用链都变成异步的。这种情况可能导致代码变得更复杂,不易维护。

类似于C# try catch的层层上抛,在某一层catch

观点

查了很多资料 ,对于这个问题说法还都不一样

  1. async/await异步传染性无法解决
  2. 可以解决但开销不小/ 解决代价不大

无法解决

在node 的層面沒法解決, 除非v8 或者jscore等等提供 GMP 模型+Socket 勾子。

不是该考虑怎么消除async,而是该考虑怎么在需要的时候给任意程序入口加一个异步上下文。除此之外任何想要在程序中段消除async的需求都是伪需求。

可以解决

  • 合理分层:将异步操作集中在特定层次,例如数据访问层或API调用层,这样可以将异步操作限制在这些层次,而不会传播到整个代码库。在这种架构下,其他层次的代码可以保持同步方式处理数据,从而降低代码复杂度。
  • 使用Promise:当使用async和await时,实际上底层是基于Promise的。你可以尽量使用Promise链式调用(.then()和.catch()),在某种程度上减少异步传染性。但请注意,过度使用Promise链式调用可能导致回调地狱问题。
  • 使用事件驱动:异步传染性问题有时候是因为代码逻辑紧密耦合所导致的。考虑使用事件驱动架构,通过发布和订阅事件的方式来解耦代码。这样,即使某个操作是异步的,它也不会影响到其他操作的同步性。
  • 限制异步操作的范围:尽量让异步操作独立,不要过多地依赖其他异步操作的结果。如果确实需要依赖其他异步操作的结果,尝试将这些操作分组,并使用Promise.all()或Promise.race()等方法来处理。
  • 避免不必要的异步操作:不要将原本可以用同步方式实现的操作变成异步操作。异步操作应该只用于真正需要的场景,例如网络请求、文件读写等。
  • ES2021 可以用top-level await
  • 封装异步操作:将需要异步操作的函数封装成一个单独的函数,该函数内部使用 async/await
    来处理异步逻辑。然后,在需要调用这个异步函数的地方,可以直接调用它,而不需要在调用者处添加 async/await。
  • 使用异步函数的返回值:如果调用异步函数的结果在调用者中不需要立即使用,可以简单地返回异步函数的 Promise 对象,而不是在调用者处添加async/await。然后在需要使用结果的地方,再使用 async/await 处理。
  • 使用回调函数:如果不适合使用async/await,可以考虑使用回调函数的方式处理异步操作。将异步函数的回调函数传递给异步函数,在回调函数中处理结果。

以下是一个简单的示例,展示了如何将异步操作限制在数据访问层,并使用事件驱动来解耦代码:

数据访问层(使用异步操作):

// dataAccessLayer.js
import axios from "axios";export const fetchData = async (url) => {try {const response = await axios.get(url);return response.data;} catch (error) {throw new Error(`Error fetching data: ${error.message}`);}
};

事件处理器(处理数据访问层的结果,发布事件):

// eventHandler.js
import EventEmitter from "events";
import { fetchData } from "./dataAccessLayer";export const eventEmitter = new EventEmitter();export const fetchAndEmitData = async (url) => {try {const data = await fetchData(url);eventEmitter.emit("dataFetched", data);} catch (error) {eventEmitter.emit("dataFetchError", error);}
};

主逻辑(订阅事件,处理事件结果):

// main.js
import { fetchAndEmitData, eventEmitter } from "./eventHandler";const onDataFetched = (data) => {console.log("Data fetched successfully:", data);
};const onDataFetchError = (error) => {console.error("Data fetch error:", error.message);
};// 订阅事件
eventEmitter.on("dataFetched", onDataFetched);
eventEmitter.on("dataFetchError", onDataFetchError);// 触发数据请求
fetchAndEmitData("https://api.example.com/data");

在这个示例中,我们将异步操作限制在了dataAccessLayer.js中。eventHandler.js负责处理这些异步操作的结果,并发布相应的事件。main.js则订阅这些事件并处理它们的结果。这样,我们在主逻辑中避免了使用async和await,从而降低了代码复杂度。

还有一种解决方案很有意思,是利用异常捕获达成的,对其可行性表示怀疑

async function getUser() {return await fetch('./1.json');
}async function m1() {const user = await getUser();return user;
}async function m2() {const user = await m1();return user;
}
async function m3() {const user = await m2();return user;
}async function main() {const user = await m3();console.log(user);
}

从上面的函数调用可以看出来,getUser是异步函数,所有使用和相关联的函数都必须使用async/await变成异步函数,这样使用也没有什么问题,但是在函数式编程环境中就不合适了。
本来这些函数应该是一个纯函数的,却因为异步具有传染性,导致这些函数全部变成副作用的了,这在函数式编程环境中是很难接受的。

所以如何不去改动这些函数,把这些异步全部去掉呢?变成没有异步的样子,从而保持这些函数的纯度。如下:

 function getUser() {return fetch('./1.json');
}function m1() {const user = getUser();return user;
}function m2() {const user = m1();return user;
}function m3() {const user = m2();return user;
}function main() {const user = m3();console.log(user);
}

怎么操作呢?getUser调用了fetch请求,导致了异步的产生。
网络传输是需要时间的,这个是无法改变的。让浏览器完全阻塞那就卡死了,肯定是行不通的。
目前的函数调用流程如下:
在这里插入图片描述
main->getUser->fetch - -> 等待网络请求,请求完成 --> getUser->main

由于fetch需要等待导致所有相关的函数都要等待。那么只能在fetch这里做一些操作了。如何让fetch不等待,就只能报错了。
在这里插入图片描述
我们看下通过fetch报错如何解决这个问题。

main->getUser->fetch->error
拿到结果存入cache: main->getUser->fetch->cache->getUser->main

在调用fetch的时候不等待了而是报错,这样所有函数都终止了,调用栈层层弹出,调用结束。

但是我们最终的目的是要拿到结果的,前面虽然报错了,网络线程仍然还在继续网络请求它不会停止,直到拿到结果。

拿到结果后我们把它放在一个缓存中,接着再去恢复整个调用链的执行。再执行fetch时,结果已经缓存在cache了,取出数据就可以直接交付不用等待了从而变成了同步函数。

整个过程会走两次,第一次以错误结束,第二次以成功结束,这两次都是同步的。

在这个过程中fetch的逻辑就发生了变化:
fetch时要判断是否有缓存,如果有缓存则返回缓存,如果没有缓存则发送真实请求同时抛出错误,然后把请求的结果保存。抛出的错误为发送请求返回的Promise对象,目的是为了在请求完成后再去恢复调用。

伪代码实现如下:

function run(func) {let cache = {status: 'pending',value: null};const oldFetch = window.fetch;window.fetch = function(...args){if(cache.status == 'fulfilled'){return cache.value;}else if(cache.status == 'rejected'){//之前的请求有问题throw cache.value;}else{// 1. 发送真是请求const promise = oldFetch(...args).then(res=>{cache.status = 'fulfilled';cache.value = res;}, err=> {cache.status = 'rejected';cache.value = err;});// 2. 抛出错误throw promise;}}// 执行入口函数try {func();} catch (error) {if(error instanceof Promise) {// 不论成功还是失败都重新调用error.then(func,func).finally(()=>{//恢复原来的值window.fetch = oldFetch;});}}}
run(main);

来源

在前端开发中如何消除异步的传染性?
消除async/await异步的传染性
如何解决 async/await 的传染性?

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

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

相关文章

WSL2 docker GUI 界面

在 WSL2 docker 中运行GUI界面。 具体流程和远程显示Ubuntu界面类似,链接, 更简单一点, 少了 ssh 的部分。 安装好wsl2 和 docker wsl2 运行GUI程序,windows 会默认弹出窗口。 可以安装 gedit 测试一下 windows 下载并运行 Xlaunch. 运行 d…

轻松配置PPPoE连接:路由器设置和步骤详解

在家庭网络环境中,我们经常使用PPPoE(点对点协议过夜)连接来接入宽带互联网。然而,对于一些没有网络专业知识的人来说,配置PPPoE连接可能会有些困难。在本文中,我将详细介绍如何轻松配置PPPoE连接&#xff…

FO-like Transformation in QROM Oracle Cloning

参考文献: [RS91] Rackoff C, Simon D R. Non-interactive zero-knowledge proof of knowledge and chosen ciphertext attack[C]//Annual international cryptology conference. Berlin, Heidelberg: Springer Berlin Heidelberg, 1991: 433-444.[BR93] Bellare M…

Python知识碎片补充【侯小啾python领航班系列(十四)】

Python知识碎片补充【侯小啾python领航班系列(十四)】 大家好,我是博主侯小啾, 🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ�…

与原有视频会议系统对接

要实现与原有视频会议系统对接,需要确保通信协议的一致性。连通宝视频会议系统可与第三方视频会议系统对接。实现与第三方会议系统对接还可以使用会议室连接器,可以确保不同系统之间的数据传输和交互。 具体对接流程可能因不同品牌和类型的视频会议系统而…

Matlab 点云线性指数计算(加权)

文章目录 一、简介二、实现代码三、实现效果参考资料一、简介 思路其实很简单,即对每个邻近点集中的点,根据其到点集中心的距离进行加权处理(权重函数),之后再基于加权之后的点获取其协方差矩阵,最后再求取其相关的特征值,以此来获取该点的线性指数。相关公式如下所示:…

Python+Requests模块添加cookie

请求中添加cookies 对于某些网站,登录然后从浏览器中获取cookies,以后就可以直接拿着cookie登录了,无需输入用户 名密码。 一、在参数中添加cookie 在发送请求时使用cookies 代码示例: import requests # 1,在参数…

uniapp中解决swiper高度自适应内容高度

起因:uniapp中swiper组件swiper 标签存在默认高度是 height: 150px ;高度无法实现由内容撑开,在默认情况下,swiper盒子高度显示总是 150px 解决办法思路: 动态设置swiper盒子的高度,故需要获取swiper-item盒…

WPF Mvvm模式下面如何将事件映射到ViewModel层

前言 平常用惯了Command绑定,都快忘记传统的基于事件编程模式了,但是Commond模式里面有个明显的问题,就是你无法获取到事件源的参数。很多大聪明肯定会说,这还不简单,通过自己写控件,给控件加个自定义属性不就行了,想要啥事件就写啥事件进去,完全自主可控。但是对于写…

建设银行RPA应用实践

当下,银行业正在从“互联网金融”时代向“新科技金融”时代迈进,在目前经济形势严峻、人力成本持续增加的经营背景下,以科技解放人力将是智能化银行发展的必然趋势。RPA技术为解决上述问题提供了崭新的路径。 RPA(机器人流程自动…

ES-深入理解倒排索引

倒排索引 idproductdesc1新版 小米 至尊-纪念版手机1小米 NFC 手机3NFC手机4小米 耳机5华为 耳机6扫地机器人7华为 Mata………………term_indexterm dictionaryposting list------------------------------------小米1……100W华为6,7,9NFC76,90耳机5352红米643,98机器人645,9…

Python中进行特征重要性分析的8个常用方法

更多资料获取 📚 个人网站:ipengtao.com 在机器学习和数据科学领域,理解特征在模型中的重要性对于构建准确且可靠的预测模型至关重要。Python提供了多种强大的工具和技术,能够探索特征重要性的各个方面。 本文将详细介绍8种常用…

记录一次爱快路由ACL策略引起的大坑

环境: A公司和B公司采用爱快的ipsec互联 B公司同时有加密软件限制网络 问题:对方ERP无法连接我们的数据库服务器 先简单测试了下1433端口是不是通的 下面的测试结果,直接ping是通的,但是加上1433端口后就不通 排查过程&#xff1…

《功能磁共振多变量模式分析中空间分辨率对解码精度的影响》论文阅读

《The effect of spatial resolution on decoding accuracy in fMRI multivariate pattern analysis》 文章目录 一、简介论文的基本信息摘要 二、论文主要内容语音刺激的解码任务多变量模式分析(MVPA)K空间 空间分辨率和平滑对MVPA的影响平滑的具体过程…

PHP实践:用ThinkPHP6完整实现用户分页功能

🏆作者简介,黑夜开发者,CSDN领军人物,全栈领域优质创作者✌,CSDN博客专家,阿里云社区专家博主,2023年6月CSDN上海赛道top4。 🏆数年电商行业从业经验,历任核心研发工程师…

Linux基本指令(后篇)

目录 14.时间指令date 15.Cal指令 16.find指令(非常重要) 17.grep指令 18.打包压缩指令zip和tar以及解压指令unzip和tar 14.时间指令date date(显示当前时间) 1.在显示方面,使用者可以设定欲显示的格式,格式设定为一个加号后接数个标记,其中…

SSM项目实战-mapper实现

1、SysUserMapper.java package com.atguigu.schedule.mapper; import com.atguigu.schedule.pojo.SysUser; import org.springframework.stereotype.Repository; Repository public interface SysUserMapper {SysUser getSysUser(SysUser sysUser); }2、ScheduleMapper.java p…

拉新地推任务管理分销助手公众号开发

拉新地推任务管理分销助手公众号开发 拉新地推任务管理分销助手公众号开发功能可以帮助企业进行地推任务的管理和分销助手的开发。以下是一些可能的功能介绍: 任务管理:这个功能可以让企业创建、分配和管理地推任务。管理员可以创建地推任务&#xff0c…

Java流处理之序列化和打印流

文章目录 序列化概述ObjectOutputStream类构造方法序列化操作 ObjectInputStream类构造方法反序列化操作1**反序列化操作2** 案例:序列化集合案例分析案例实现 打印流概述PrintStream类构造方法改变打印流向 序列化 概述 Java 提供了一种对象序列化的机制。用一个…

【【FPGA 之 MicroBlaze定时器中断实验】】

FPGA 之 MicroBlaze定时器中断实验 AXI Timer 具有 AXI 总线接口,能够产生不同时间周期和占空比的时钟、脉冲产生电路、产生与时间有关的中断和用于电机控制的脉宽调制信号。 AXI Timer IP 核提供了一个 AXI4 Lite 接口用于与处理器通信;它内部有两个可…