Web - 使用AbortController解决竞争问题

在开发应用程序时,数据获取是最基本的任务之一。 尽管如此,还是有一些事情需要注意:其中之一是竞争条件。本文解释了它是什么,并提供了使用 AbortController 的解决方案。

什么是竞争条件

竞争条件”是指我们的应用程序依赖于一系列事件,但它们的顺序是不可控的,这很有可能发生在异步代码中。比如下面这个例子:

这个组件很简单,就是根据ID去获取一个文章,把它显示出来。但是,这里在某些情况下会有个问题:

  1. 用户打开 /posts/1 以查看第一篇文章,我们开始获取 id 为 1 的帖子,有一些互联网连接问题,帖子还没有加载。
  2. 这时用户不等待第一个帖子加载完成,直接将页面更改为 /posts/2。那么我们开始获取 id 为 2 的帖子,这次网络没有问题,几乎可以立即使用。
  3. 但此时第一篇文章也加载完毕,setPost(fetchedPost) 执行,用第一个帖子覆盖当前状态,即使用户将页面切换到 /posts/2,但内容却是ID=1的文章。

对上述问题最直接的解决方法是引入 didCancel 变量。React 将在我们的组件卸载时将它设为true。

虽然上述解决方案解决了问题,但它并不是最优的。 浏览器仍然等待第一次的 HTTP 请求完成但忽略其结果。 为了改善这一点,我们可以使用 AbortController。

使用AbortController

有了它,我们可以中止一个或多个获取请求。 为此,我们需要创建 AbortController 的实例并在发出 fetch 请求时使用它。

上面,我们通过 fetch 选项传递 abortController.signal。这样浏览器可以在我们调用 abortController.abort() 时停止请求。

具体过程如下:

  1. 用户打开 /posts/1 以查看第一篇文章,我们开始获取 id 为 1 的帖子。
  2. 不等待第一个帖子,用户将页面更改为/posts/2
  3. useEffect 在上一篇文章之后清理并运行 abortController.abort(),
  4. 接下来我们开始获取 id 为 2 的帖子,帖子加载没有问题,立刻加载成功
  5. 然而第一篇文章永远不会完成加载,因为我们加载第二篇文章的时候已经中止了第一个请求。

我们可以在开发者工具的网络选项卡中观察到上述行为。

调用 abortController.abort() 的另一个关键是它会导致 fetch Promise被Reject。它可能会导致未捕获的错误。为了避免出现那种情况,我们需要捕获这种错误。

使用AbortController取消其它的Promise

除了使用 AbortController 来取消请求之外,我们还可以在函数中使用它。 让我们创建一个等待函数,它返回一个可以看到这个的Promise。

我们可以修改我们的等待函数以接受类似于 fetch 函数的信号属性。 为此,我们需要利用信号在调用 abortController.reject() 时发出 abort 事件这一事实。

现在,我们需要传递信号属性来等待和中止Promise。

总结:

我们了解了如何使用AbortController去取消请求,取消自定义的promise,大家可以试试,有问题,请留言,欢迎一起讨论。

Web
发表评论
留言与评论(共有 0 条评论) “”
   
验证码:

相关文章

推荐文章