日韩成人免费在线_国产成人一二_精品国产免费人成电影在线观..._日本一区二区三区久久久久久久久不

當前位置:首頁 > 科技  > 軟件

React 19 出手解決了異步請求的競態問題,是好事還是壞事?

來源: 責編: 時間:2024-06-07 17:20:19 218觀看
導讀是的,又是競態問題。在客戶端開發中,這是一個老生常態的問題。一個有經驗的前端工程師必定是對這個問題的情況與解決方案如數家珍。因此競態問題也經常在面試的過程中被討論。競態問題指的是,當我們在交互過程中,由于各種

uXM28資訊網——每日最新資訊28at.com

是的,又是競態問題。uXM28資訊網——每日最新資訊28at.com

在客戶端開發中,這是一個老生常態的問題。一個有經驗的前端工程師必定是對這個問題的情況與解決方案如數家珍。因此競態問題也經常在面試的過程中被討論。uXM28資訊網——每日最新資訊28at.com

競態問題指的是,當我們在交互過程中,由于各種原因導致同一個接口短時間之內連續發送請求,后發送的請求有可能先得到請求結果,從而導致數據渲染出現預期之外的錯誤。uXM28資訊網——每日最新資訊28at.com

有的地方也稱為競態條件uXM28資訊網——每日最新資訊28at.com

因為防止重復執行可以有效的解決競態問題,因此許多時候面試官也會直接在面試中問我們如何實現防重。常用的方式就是取消上一次請求,或者設置狀態讓按鈕不能連續點擊,想必各位大佬對這些方案都已經非常熟悉,我這里就不展開細說。當然,這個問題雖然被經常討論,但是要解決好確實需要一點技術功底。uXM28資訊網——每日最新資訊28at.com

React 19 結合 Suspense 也在競態問題上,提出了一個自己的解決方案。我們結合新的案例來探討一下這個問題,看完之后大家感受一下這種方式是好是壞。uXM28資訊網——每日最新資訊28at.com

一、案例

我們先來看一下本次案例要實現的交互效果。如下圖所示。每次點擊會新增一條數據到下方的列表中。uXM28資訊網——每日最新資訊28at.com

uXM28資訊網——每日最新資訊28at.com

我們來實現一下這個效果,首先定義一個用于請求接口的 promise。uXM28資訊網——每日最新資訊28at.com

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

然后和前面的案例一樣,我們將每次點擊的 api 作為狀態存儲起來,通過 api 的改變來觸發更新的執行。uXM28資訊網——每日最新資訊28at.com

const [api, setApi] = useState(null)

與此同時,我們還需要一個數組作為狀態來管理列表。uXM28資訊網——每日最新資訊28at.com

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

有了這個數組之后,我們需要遍歷這個數組渲染成 UI。uXM28資訊網——每日最新資訊28at.com

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

最后需要 loading 顯示的部分,我們使用 Suspense 來完成。uXM28資訊網——每日最新資訊28at.com

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

需要注意的是,我們這里把 setList 傳遞進入了子組件。這個細節需要仔細思考我的動因。uXM28資訊網——每日最新資訊28at.com

我們要考慮的問題是,當我們在 Suspense 之外,需要知道請求成功的狀態和數據時,只有在 Suspense 的子組件內部才可以獲取到。Suspense 子組件和外面的 Loading 是一個互斥的顯示關系。uXM28資訊網——每日最新資訊28at.com

因此,我們要在子組件內部去獲取請求成功的數據結果。uXM28資訊網——每日最新資訊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 是為了讓最后一條數據在列表中顯示,而不在這里顯示。uXM28資訊網——每日最新資訊28at.com

這里我們使用了 useEffect 來表示子組件渲染完成時需要執行的邏輯。注意 React 19 雖然通過很多方式大幅度弱化了 useEffect 的存在感,但是偶爾在合適的時候使用也是必要的。uXM28資訊網——每日最新資訊28at.com

我在合并 list 的過程中,添加了一個判斷。uXM28資訊網——每日最新資訊28at.com

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

這個細節在真實項目開發中尤其重要。因為 React 19 嚴格模式之下,組件會讓 useEffect 執行兩次,以模擬生產環境的重復請求問題,因此,我這里做了一個判斷方式同樣的數據連續推送到數組里,從而導致線上 bug 的發生。uXM28資訊網——每日最新資訊28at.com

一個程序員是否經驗豐富,是否成熟,都是體現在這些生產環境的細節中。uXM28資訊網——每日最新資訊28at.com

完整代碼如下:uXM28資訊網——每日最新資訊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>  )}

這樣之后,我們的目標基本就完成了。接下來,我們需要觀察,當我惡意重復點擊按鈕,會發生什么事情。uXM28資訊網——每日最新資訊28at.com

二、連續點擊

惡意連續點擊之前,我根據我以往的經驗預測一下可能會發生什么事情。uXM28資訊網——每日最新資訊28at.com

首先,多次點擊會導致多次請求,因此數組中會新增大量的數據。uXM28資訊網——每日最新資訊28at.com

其次,由于請求太密集,那么點擊的先后順序,與請求成功的先后順序不一致,因此列表中的順序也會與點擊順序不同。「競態問題」uXM28資訊網——每日最新資訊28at.com

那么我們來試著操作一下,看看該案例會有什么反應。演示結果如下,新增一條數據時,我連續點擊了 10 次。uXM28資訊網——每日最新資訊28at.com

圖片uXM28資訊網——每日最新資訊28at.com

結果我們發現,點擊期間,并沒有新的數據渲染到頁面上,一直是 loading 的狀態。uXM28資訊網——每日最新資訊28at.com

再來看一下此時的請求情況。uXM28資訊網——每日最新資訊28at.com

uXM28資訊網——每日最新資訊28at.com

請求的順序被嚴格控制了:上一個請求請求成功之后,下一個請求才開始發生。此時是一個串行的請求過程。uXM28資訊網——每日最新資訊28at.com

react 19 使用這種思路解決了競態問題。與此同時,反饋到數據上,雖然前面多次的請求已經成功,但是對于組件狀態來說,這個中間過程中一直有請求在發生,此時 React 認為中間的請求產生的數據為無效數據。只會把最后一個請求成功的數據作為最終的返回結果。uXM28資訊網——每日最新資訊28at.com

三、是好是壞

很顯然,僅從 UI 結果上來說,這樣的處理方式確實是非常合理的,我們不需要過多的干涉數據的處理,非常的輕松。但問題是,每次請求都成功發生。uXM28資訊網——每日最新資訊28at.com

當我點擊 10 次,就會有 10 次請求,由于使用串行的策略來解決競態問題,導致最后一次的請求結果需要等待很長實踐才會返回。這無疑極大的降低了開發體驗。uXM28資訊網——每日最新資訊28at.com

和取消上一次的請求相比,無論是從體驗上,還是從效率上來說,無疑都是更差的一種方案。uXM28資訊網——每日最新資訊28at.com

因此,我們可以簡單基于目前的代碼,使用禁用按鈕的方式,來防止重復請求。uXM28資訊網——每日最新資訊28at.com

在父組件中定義一個狀態用于控制按鈕的禁用狀態。uXM28資訊網——每日最新資訊28at.com

const [disabled, setDisabled] = useState(false)

并將其傳遞給按鈕 button 組件的 disabled 屬性。uXM28資訊網——每日最新資訊28at.com

<button   disabled={disabled}   onClick={__clickToGetMessage}>新增數據</button>

點擊時,我們將其設置為 true,此時一個新的請求會發生。uXM28資訊網——每日最新資訊28at.com

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

請求成功之后,我們在子組件的 useEffect 中,將其設置為 false。子組件代碼調整如下:uXM28資訊網——每日最新資訊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>  )}

演示效果如下:uXM28資訊網——每日最新資訊28at.com

uXM28資訊網——每日最新資訊28at.com

這種方式也可以比較合理的解決競態問題。uXM28資訊網——每日最新資訊28at.com

后續我們通過別的案例,再來演示通過取消上一次的接口請求方式是如何實現的。uXM28資訊網——每日最新資訊28at.com

本文鏈接:http://www.www897cc.com/showinfo-26-92747-0.htmlReact 19 出手解決了異步請求的競態問題,是好事還是壞事?

聲明:本網頁內容旨在傳播知識,若有侵權等問題請及時與本網聯系,我們將在第一時間刪除處理。郵件:2376512515@qq.com

上一篇: 大廠真實案例,CPU 升高問題如何排查?五分鐘掌握

下一篇: 從 Dapper 到 OpenTelemetry:分布式追蹤的演進之旅

標簽:
  • 熱門焦點
Top 主站蜘蛛池模板: 保德县| 富宁县| 岳普湖县| 曲松县| 泾源县| 柳河县| 钟祥市| 满城县| 潼关县| 丰都县| 营山县| 兖州市| 海盐县| 雅安市| 杭锦后旗| 海口市| 凤城市| 红安县| 长治市| 紫金县| 尚志市| 寿光市| 汕头市| 凤山市| 东丽区| 和林格尔县| 淮南市| 德安县| 武义县| 彭泽县| 汾西县| 黔江区| 南雄市| 诸暨市| 河西区| 军事| 宜阳县| 浦东新区| 塘沽区| 河源市| 博乐市|