本文介绍: “异步传染性”问题通常是指,当一个函数使用asyncawait,其调用者需要使用asyncawait处理异步操作,导致整个调用链都变成异步的。这种情况可能导致代码变得更复杂,不易维护。类似于C# try catch的层层上抛,在某一层catch

概述

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

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

观点

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

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

无法解决

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

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

可以解决

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

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

// 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 的传染性?

原文地址:https://blog.csdn.net/weixin_44231544/article/details/134398859

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任

如若转载,请注明出处:http://www.7code.cn/show_25136.html

如若内容造成侵权/违法违规/事实不符,请联系代码007邮箱suwngjj01@126.com进行投诉反馈,一经查实,立即删除

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注