前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >那些消除异步的传染性的方法到底可不可取?

那些消除异步的传染性的方法到底可不可取?

作者头像
友儿
发布2024-08-15 14:20:25
1000
发布2024-08-15 14:20:25
举报
文章被收录于专栏:友儿

最近看到了一个非常有意思的所谓的大厂的面试题,在网上也引起了很大的争议。那就是async/await消除异步的传染性,以fetch函数为例,通过侵入式修改fetch方法,来实现消除异步的传染性的方案。

这种方案其实是一股脑的借鉴一些框架的实现,如react框架中的父组件加载子组件的实现。

  • 在react环境中是大量应用这种方式的。react内置组件Suspense,它的作用就是当它子组件出现异步的时候可以等待,并在fallback属性显示一个等待的提示或loading。Suspense内部会捕获promise错误,一旦捕获了就会等待promise完成,在等待期间就会渲染fallback内容,直到promise完成再重新去渲染,也就是会重新调用一次这个函数组件得到新的内容。

但是框架这样设计有框架的定位,我们可以根据这种方法扩展思维,但是不建议在生产环境中使用。这种方法其实仅仅只是为了消除所谓的传染性,对于实际业务实现没有实质效益。

接下来看下实现思路吧

当一个 fetch 请求返回 promise 时,需要使用 await 来获取数据。而一旦使用了 await,当前函数就必须是 async 函数。如此循环往复地调用,接下来所有的方法都得加上 async await,这就是所谓的“异步传染性”。

代码语言:javascript
复制
async function reqRes() {
    return await fetch('https://jsonplaceholder.typicode.com/posts/1');
}

async function async() {
    return await reqRes();
}

async function main() {
    let user = await a();
    console.log(user)
}

main();

如何消除上面的现象呢?这就不得不说下面的这种方案啦。

由于fetch需要等待导致所有相关的函数都要等待,那么只能在fetch这里做一些操作了,如何让fetch不等待,就只能报错了。

在调用fetch的时候不等待了而是报错,这样所有函数都终止了,调用栈层层弹出,调用结束。但是我们最终的目的是要拿到结果的,前面虽然报错了,网络线程仍然还在继续网络请求它不会停止,直到拿到结果。

拿到结果后我们把它放在一个缓存中,接着再去恢复整个调用链的执行。再执行fetch时,结果已经缓存在cache了,取出数据就可以直接交付不用等待了从而变成了同步函数。整个过程会走两次,第一次以错误结束,第二次以成功结束,这两次都是同步的。

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

代码实现

代码语言:javascript
复制
 function start(func) {
        const oldFetch = fetch;
        const cache = {
            status: 'PENDING',
            value: null
        }

        function newFetch(...args) {
            if (cache.status === 'FULFILLED') {
                return cache.value;
            }
            if (cache.status === 'REJECTED') {
                throw  cache.value;
            }
            throw oldFetch(...args).then(function (res) {
                return res.json();
            }).then(function (data) {
                cache.status = 'FULFILLED';
                cache.value = data;
            }).catch(function (error) {
                cache.status = 'REJECTED';
                cache.value = error;
            });
        }

        window.fetch = newFetch;

        try {
            func();
        } catch (error) {
            if (error instanceof Promise) {
                error.finally(function () {
                    window.fetch = newFetch;
                    func();
                    window.fetch = oldFetch;
                })
            }
        }
        window.fetch = oldFetch;
    }

    function reqRes() {
        return fetch('https://jsonplaceholder.typicode.com/posts/1');
    }

    function a() {
        return reqRes();
    }

    function main() {
        console.log('main');
        let res = a();
        console.log(res)
    }
    start(main)

对代码的进一步分析

start 函数中:

  • 它先保存了原始的 fetch 方法。
  • 定义了一个 cache 对象来记录异步操作的状态和结果。
  • newFetch 函数根据 cache 的状态来决定直接返回结果或执行原始 fetch 并处理其后续的解析和状态更新。如果遇到错误则更新状态并抛出。
  • 然后在执行 func(即 main 函数)时,如果遇到一个 Promise 类型的异常,在其 finally 中先恢复 newFetch ,重新执行 func ,最后再恢复原始 fetch

整体来说

  • 这个代码试图通过一些自定义的逻辑来控制和管理异步 fetch 操作及其相关的状态和流程,可能是为了在特定场景下实现一些特殊的行为或控制机制。但这种方式可能会引入一些复杂性和潜在的问题,比如对 fetch 的修改可能会影响到其他依赖于标准 fetch 行为的部分,并且异常处理的方式也需要谨慎考虑其正确性和合理性。
本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档