消除异步的传染性

484 阅读4分钟

在面试中我们可能会遇到许多面试题是我们平常没去怎么思考过的题目,这种题目如果能回答好的的话将会是一加分题。接下来我们看看这个面试题,它是一个大厂内部的晋升考试题,我们先来看看这道题。 这道题要处理的是异步的传染性,我们先来看看下面的代码

carbon (1).png

我们可以看到上面的函数封装了一个ajax请求,他里面用了await,如果说另外一个函数要用这个函数,它也得await,其他函数用也是要变成异步。我们可以发现一个节点发生了异步,这个节点上所有的调用链全得异步。如果这个在函数式编程环境中就会很恶心,本来这些函数上好好的纯函数,结果全部变成副作用。如果我们要去除这种异步的传染性怎么来处理呢? 首先问题的关键是是一开始请求的函数,由于我们代码变成同步了,这个函数就必须要立马返回结果,但是它不行,它远程请求需要时间,我们要求它立即返回结果,但是它返回不了就会报错。我们可以以这个点来作为突破口。

9adf7ae5cc55658c533ae70e4f66a7ae.png 我们可以以这个点来设计,首先函数开始,然后调用了fetch,如果引发函数就结束,然后网络请求在后台进行,如果拿到数据,我们把这个数据缓存到某个地方,然后我们重新调用这个函数再来执行一遍,也就是这个函数会被执行两次。 当第二次执行这个函数的时候,前面已经缓存了data,于是在调用这个fetch的时候,它就直接把缓存的数据给到我们,然后函数继续运行,然后就结束了。 我们来看看怎么来改造这个函数,首先我们不能对原来的函数有侵入性,我们就要在终点的main函数来做文章。我们先写一个函数,把main放到里面来运行,我们在运行这个函数之前,改动fetch函数,fetch函数里面是发送请求,然后是报错,如果有缓存,就给到我们数据。我们先来定义一个变量,因为fetch可能多次调用,所有我们最好使用数组let cache = [].我们用一个i来表示调用期间第几次调用fetch,调用一次i就增加1let i = 0,我们怎么来判断有没有缓存呢,就要判断cache[i]是否存在,如果存在就给到我们数据,如果不存在,我们就定义一个结果。然后这个缓存我们需要缓存什么呢? 首先异步通信需要的信息就是状态,它通信完成没有,然后就是通信完成之后数据是什么,还有就是有没有错误。 如果需要交付缓存结果需要怎么交付呢?我们先看缓存状态,如果状态完成是fulfilled,那我们就返回这个缓存数据,如果状态是rejected,也就是状态有问题,我们就抛出错误。

carbon (2).png

然后就是发送请求了,首先我们发送请求是用原来的fetch去发送请求const _orignalFetch = window.fetch,然后请求成功缓存就好了,失败的时候做记录,把里面err传给缓存结果

carbon (4).png

最后就是报错了,如果报错我们怎么重新启动执行函数?我们使用try catch收到它执行期间发生的错误,但是它执行期间可能会发生很多错误,不是所有的错误都需要重新执行一遍,只有特定的错误才需要引发重新执行,而且在这错误中我们还需要知道什么时候引发重新执行。

carbon (5).png

下面就是完整代码

carbon (6).png

react里面已经大量使用了这种思路,例如Suspense里面子组件ProfileDetails的状态,请求中就显示loading,返回结果后就显示用户详情。React中ProfileDetails组件没有返回Promise,就是一个普通的组件。如果我们在里面打印一个1,1会打印两次。和上面的原理一样,先执行ProfileDetails,会打印第一次,直接throw一个promoise的异常。成功后继续再执行一次,然后打印第二次。 以上就是消除异步传染性的全部内容了,另外xdm可以每年定期出来面面试,衡量一下自己当前的技术水平以及价值,说不定还能面试上你心怡的公司。 文章就到这里了,拜拜~