当前位置:首页 > 科技  > 软件

React 19 出手解决了异步请求的竞态问题,是好事还是坏事?

来源: 责编: 时间:2024-06-07 17:20:19 152观看
导读是的,又是竞态问题。在客户端开发中,这是一个老生常态的问题。一个有经验的前端工程师必定是对这个问题的情况与解决方案如数家珍。因此竞态问题也经常在面试的过程中被讨论。竞态问题指的是,当我们在交互过程中,由于各种

epf28资讯网——每日最新资讯28at.com

是的,又是竞态问题。epf28资讯网——每日最新资讯28at.com

在客户端开发中,这是一个老生常态的问题。一个有经验的前端工程师必定是对这个问题的情况与解决方案如数家珍。因此竞态问题也经常在面试的过程中被讨论。epf28资讯网——每日最新资讯28at.com

竞态问题指的是,当我们在交互过程中,由于各种原因导致同一个接口短时间之内连续发送请求,后发送的请求有可能先得到请求结果,从而导致数据渲染出现预期之外的错误。epf28资讯网——每日最新资讯28at.com

有的地方也称为竞态条件epf28资讯网——每日最新资讯28at.com

因为防止重复执行可以有效的解决竞态问题,因此许多时候面试官也会直接在面试中问我们如何实现防重。常用的方式就是取消上一次请求,或者设置状态让按钮不能连续点击,想必各位大佬对这些方案都已经非常熟悉,我这里就不展开细说。当然,这个问题虽然被经常讨论,但是要解决好确实需要一点技术功底。epf28资讯网——每日最新资讯28at.com

React 19 结合 Suspense 也在竞态问题上,提出了一个自己的解决方案。我们结合新的案例来探讨一下这个问题,看完之后大家感受一下这种方式是好是坏。epf28资讯网——每日最新资讯28at.com

一、案例

我们先来看一下本次案例要实现的交互效果。如下图所示。每次点击会新增一条数据到下方的列表中。epf28资讯网——每日最新资讯28at.com

epf28资讯网——每日最新资讯28at.com

我们来实现一下这个效果,首先定义一个用于请求接口的 promise。epf28资讯网——每日最新资讯28at.com

const getApi = async () => {  const res = await fetch('https://api.chucknorris.io/jokes/random')  return res.json()}

然后和前面的案例一样,我们将每次点击的 api 作为状态存储起来,通过 api 的改变来触发更新的执行。epf28资讯网——每日最新资讯28at.com

const [api, setApi] = useState(null)

与此同时,我们还需要一个数组作为状态来管理列表。epf28资讯网——每日最新资讯28at.com

const [list, setList] = useState([])

有了这个数组之后,我们需要遍历这个数组渲染成 UI。epf28资讯网——每日最新资讯28at.com

<div className="list">  {list.map((item, index) => {    return <div className='item' key={item}>{item}</div>  })}</div>

最后需要 loading 显示的部分,我们使用 Suspense 来完成。epf28资讯网——每日最新资讯28at.com

<Suspense fallback={<div>loading...</div>}>  <Item api={api} setList={setList} /></Suspense>

需要注意的是,我们这里把 setList 传递进入了子组件。这个细节需要仔细思考我的动因。epf28资讯网——每日最新资讯28at.com

我们要考虑的问题是,当我们在 Suspense 之外,需要知道请求成功的状态和数据时,只有在 Suspense 的子组件内部才可以获取到。Suspense 子组件和外面的 Loading 是一个互斥的显示关系。epf28资讯网——每日最新资讯28at.com

因此,我们要在子组件内部去获取请求成功的数据结果。epf28资讯网——每日最新资讯28at.com

const Item = ({api, setList}) => {  const [show, setShow] = useState(true)  const joke = api ? use(api) : {value: 'nothing'}  useEffect(() => {    if (!api) return    setList((list) => {      if (!list.includes(joke.value)) {        return list.concat(joke.value)      }      return list    })    setShow(false)  }, [])  const __cls = show ? '_03_a_value show' : '_03_a_value'  return (    <div className={__cls}>{joke.value}</div>  )}

状态 show 是为了让最后一条数据在列表中显示,而不在这里显示。epf28资讯网——每日最新资讯28at.com

这里我们使用了 useEffect 来表示子组件渲染完成时需要执行的逻辑。注意 React 19 虽然通过很多方式大幅度弱化了 useEffect 的存在感,但是偶尔在合适的时候使用也是必要的。epf28资讯网——每日最新资讯28at.com

我在合并 list 的过程中,添加了一个判断。epf28资讯网——每日最新资讯28at.com

setList((list) => {  if (!list.includes(joke.value)) {    return list.concat(joke.value)  }  return list})

这个细节在真实项目开发中尤其重要。因为 React 19 严格模式之下,组件会让 useEffect 执行两次,以模拟生产环境的重复请求问题,因此,我这里做了一个判断方式同样的数据连续推送到数组里,从而导致线上 bug 的发生。epf28资讯网——每日最新资讯28at.com

一个程序员是否经验丰富,是否成熟,都是体现在这些生产环境的细节中。epf28资讯网——每日最新资讯28at.com

完整代码如下:epf28资讯网——每日最新资讯28at.com

const getApi = async () => {  const res = await fetch('https://api.chucknorris.io/jokes/random')  return res.json()}export default function Index() {  const [api, setApi] = useState(null)  const [list, setList] = useState([])  function __clickToGetMessage() {    setApi(getApi())  }  return (    <div>      <div id='tips'>点击按钮新增一条数据,该数据从接口中获取</div>      <button onClick={__clickToGetMessage}>新增数据</button>      <div className="content">        <div className="list">          {list.map((item, index) => {            return <div className='item' key={item}>{item}</div>          })}        </div>                <Suspense fallback={<div>loading...</div>}>          <Item api={api} setList={setList} />        </Suspense>      </div>    </div>  )}const Item = ({api, setList}) => {  const [show, setShow] = useState(true)  const joke = api ? use(api) : {value: 'nothing'}  useEffect(() => {    if (!api) return    setList((list) => {      if (!list.includes(joke.value)) {        return list.concat(joke.value)      }      return list    })    setShow(false)  }, [])  const __cls = show ? '_03_a_value show' : '_03_a_value'  return (    <div className={__cls}>{joke.value}</div>  )}

这样之后,我们的目标基本就完成了。接下来,我们需要观察,当我恶意重复点击按钮,会发生什么事情。epf28资讯网——每日最新资讯28at.com

二、连续点击

恶意连续点击之前,我根据我以往的经验预测一下可能会发生什么事情。epf28资讯网——每日最新资讯28at.com

首先,多次点击会导致多次请求,因此数组中会新增大量的数据。epf28资讯网——每日最新资讯28at.com

其次,由于请求太密集,那么点击的先后顺序,与请求成功的先后顺序不一致,因此列表中的顺序也会与点击顺序不同。「竞态问题」epf28资讯网——每日最新资讯28at.com

那么我们来试着操作一下,看看该案例会有什么反应。演示结果如下,新增一条数据时,我连续点击了 10 次。epf28资讯网——每日最新资讯28at.com

图片epf28资讯网——每日最新资讯28at.com

结果我们发现,点击期间,并没有新的数据渲染到页面上,一直是 loading 的状态。epf28资讯网——每日最新资讯28at.com

再来看一下此时的请求情况。epf28资讯网——每日最新资讯28at.com

epf28资讯网——每日最新资讯28at.com

请求的顺序被严格控制了:上一个请求请求成功之后,下一个请求才开始发生。此时是一个串行的请求过程。epf28资讯网——每日最新资讯28at.com

react 19 使用这种思路解决了竞态问题。与此同时,反馈到数据上,虽然前面多次的请求已经成功,但是对于组件状态来说,这个中间过程中一直有请求在发生,此时 React 认为中间的请求产生的数据为无效数据。只会把最后一个请求成功的数据作为最终的返回结果。epf28资讯网——每日最新资讯28at.com

三、是好是坏

很显然,仅从 UI 结果上来说,这样的处理方式确实是非常合理的,我们不需要过多的干涉数据的处理,非常的轻松。但问题是,每次请求都成功发生。epf28资讯网——每日最新资讯28at.com

当我点击 10 次,就会有 10 次请求,由于使用串行的策略来解决竞态问题,导致最后一次的请求结果需要等待很长实践才会返回。这无疑极大的降低了开发体验。epf28资讯网——每日最新资讯28at.com

和取消上一次的请求相比,无论是从体验上,还是从效率上来说,无疑都是更差的一种方案。epf28资讯网——每日最新资讯28at.com

因此,我们可以简单基于目前的代码,使用禁用按钮的方式,来防止重复请求。epf28资讯网——每日最新资讯28at.com

在父组件中定义一个状态用于控制按钮的禁用状态。epf28资讯网——每日最新资讯28at.com

const [disabled, setDisabled] = useState(false)

并将其传递给按钮 button 组件的 disabled 属性。epf28资讯网——每日最新资讯28at.com

<button   disabled={disabled}   onClick={__clickToGetMessage}>新增数据</button>

点击时,我们将其设置为 true,此时一个新的请求会发生。epf28资讯网——每日最新资讯28at.com

function __clickToGetMessage() {  setDisabled(true);  setApi(getApi())}

请求成功之后,我们在子组件的 useEffect 中,将其设置为 false。子组件代码调整如下:epf28资讯网——每日最新资讯28at.com

const Item = ({api, setList, setDisabled}) => {  const [show, setShow] = useState(true)  const joke = api ? use(api) : {value: 'nothing'}  useEffect(() => {    if (!api) return+   setDisabled(false)    setList((list) => {      if (!list.includes(joke.value)) {        return list.concat(joke.value)      }      return list    })    setShow(false)  }, [])  const __cls = show ? '_03_a_value show' : '_03_a_value'  return (    <div className={__cls}>{joke.value}</div>  )}

演示效果如下:epf28资讯网——每日最新资讯28at.com

epf28资讯网——每日最新资讯28at.com

这种方式也可以比较合理的解决竞态问题。epf28资讯网——每日最新资讯28at.com

后续我们通过别的案例,再来演示通过取消上一次的接口请求方式是如何实现的。epf28资讯网——每日最新资讯28at.com

本文链接://www.dmpip.com//www.dmpip.com/showinfo-26-92747-0.htmlReact 19 出手解决了异步请求的竞态问题,是好事还是坏事?

声明:本网页内容旨在传播知识,若有侵权等问题请及时与本网联系,我们将在第一时间删除处理。邮件:2376512515@qq.com

上一篇: 大厂真实案例,CPU 升高问题如何排查?五分钟掌握

下一篇: 从 Dapper 到 OpenTelemetry:分布式追踪的演进之旅

标签:
  • 热门焦点
  • MIX Fold3包装盒泄露 新机本月登场

    MIX Fold3包装盒泄露 新机本月登场

    小米的全新折叠屏旗舰MIX Fold3将于本月发布,近日该机的真机包装盒在网上泄露。从图上来看,新的MIX Fold3包装盒在外观设计方面延续了之前的方案,变化不大,这也是目前小米旗舰
  • JVM优化:实战OutOfMemoryError异常

    JVM优化:实战OutOfMemoryError异常

    一、Java堆溢出堆内存中主要存放对象、数组等,只要不断地创建这些对象,并且保证 GC Roots 到对象之间有可达路径来避免垃 圾收集回收机制清除这些对象,当这些对象所占空间超过
  • 每天一道面试题-CPU伪共享

    每天一道面试题-CPU伪共享

    前言:了不起:又到了每天一到面试题的时候了!学弟,最近学习的怎么样啊 了不起学弟:最近学习的还不错,每天都在学习,每天都在进步! 了不起:那你最近学习的什么呢? 了不起学弟:最近在学习C
  • 8月见!小米MIX Fold 3获得3C认证:支持67W快充

    8月见!小米MIX Fold 3获得3C认证:支持67W快充

    这段时间以来,包括三星、一加、荣耀等等有不少品牌旗下的最新折叠屏旗舰都得到了不少爆料,而小米新一代折叠屏旗舰——小米MIX Fold 3此前也屡屡被传
  • iQOO 11S新品发布会

    iQOO 11S新品发布会

    iQOO将在7月4日19:00举行新品发布会,推出杭州亚运会电竞赛事官方用机iQOO 11S。
  • 机构称Q2全球智能手机出货量同比下滑11% 苹果份额依旧第2

    机构称Q2全球智能手机出货量同比下滑11% 苹果份额依旧第2

    7月20日消息,据外媒报道,研究机构的报告显示,由于需求下滑,今年二季度全球智能手机的出货量,同比下滑了11%,三星、苹果等主要厂商的销量,较去年同期均有下
  • 微软发布Windows 11新版 引入全新任务栏状态

    微软发布Windows 11新版 引入全新任务栏状态

    近日,微软发布了Windows 11新版,而Build 22563更新主要引入了几周前曝光的平板模式任务栏等,系统更流畅了。更新中,Windows 11加入了专门针对平板优化的任务栏
  • 华为举行春季智慧办公新品发布会 首次推出电子墨水屏平板

    华为举行春季智慧办公新品发布会 首次推出电子墨水屏平板

    北京时间2月27日晚,华为在巴塞罗那举行春季智慧办公新品发布会,在海外市场推出之前已经在中国市场上市的笔记本、平板、激光打印机等办公产品,并首次推出搭载
  • 英特尔Xe HPG游戏显卡:拥有512EU,单风扇版本

    英特尔Xe HPG游戏显卡:拥有512EU,单风扇版本

    据10 月 30 日外媒 TheVerge 消息报道,英特尔 Xe HPG Arc Alchemist 的正面实被曝光,不仅拥有 512 EU 版显卡,还拥有 128EU 的单风扇版本。另外,这款显卡 PCB
Top
Baidu
map