自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

React 核心團(tuán)隊(duì)成員解釋“代數(shù)效應(yīng)與 React”

新聞 前端
React核心團(tuán)隊(duì)成員Sebastian Markbåge[1](React Hooks的發(fā)明者)曾說:我們?cè)赗eact中做的就是踐行代數(shù)效應(yīng)(Algebraic Effects)。

 React核心團(tuán)隊(duì)成員Sebastian Markbåge[1](React Hooks的發(fā)明者)曾說:我們?cè)赗eact中做的就是踐行代數(shù)效應(yīng)(Algebraic Effects)。

那么,代數(shù)效應(yīng)是什么呢?它和React有什么關(guān)系呢。

[[345619]]

什么是代數(shù)效應(yīng)

代數(shù)效應(yīng)是函數(shù)式編程中的一個(gè)概念,用于將副作用從函數(shù)調(diào)用中分離。

接下來我們用虛構(gòu)的語法來解釋。

假設(shè)我們有一個(gè)函數(shù)getTotalPicNum,傳入2個(gè)用戶名稱后,分別查找該用戶在平臺(tái)保存的圖片數(shù)量,最后將圖片數(shù)量相加后返回。

  1. function getTotalPicNum(user1, user2) { 
  2.  
  3.   const num1 = getPicNum(user1); 
  4.  
  5.   const num2 = getPicNum(user2); 
  6.  
  7.   return picNum1 + picNum2; 
  8.  

在getTotalPicNum中,我們不關(guān)注getPicNum的實(shí)現(xiàn),只在乎“獲取到兩個(gè)數(shù)字后將他們相加的結(jié)果返回”這一過程。

接下來我們來實(shí)現(xiàn)getPicNum。

"用戶在平臺(tái)保存的圖片數(shù)量"是保存在服務(wù)器中的。所以,為了獲取該值,我們需要發(fā)起異步請(qǐng)求。

為了盡量保持getTotalPicNum的調(diào)用方式不變,我們首先想到了使用async await:

  1. async function getTotalPicNum(user1, user2) { 
  2.  
  3.   const num1 = await getPicNum(user1); 
  4.  
  5.   const num2 = await getPicNum(user2); 
  6.  
  7.   return picNum1 + picNum2; 
  8.  

但是,async await是有傳染性的 —— 當(dāng)一個(gè)函數(shù)變?yōu)閍sync后,這意味著調(diào)用他的函數(shù)也需要是async,這破壞了getTotalPicNum的同步特性。

有沒有什么辦法能保持getTotalPicNum保持現(xiàn)有調(diào)用方式不變的情況下實(shí)現(xiàn)異步請(qǐng)求呢?

沒有。不過我們可以虛構(gòu)一個(gè)。

我們虛構(gòu)一個(gè)類似try...catch的語法 —— try...handle與兩個(gè)操作符perform、resume。

  1. function getPicNum(name) { 
  2.  
  3.   const picNum = perform name; 
  4.  
  5.   return picNum; 
  6.  
  7.  
  8. try { 
  9.  
  10.   getTotalPicNum('kaSong''xiaoMing'); 
  11.  
  12. } handle (who) { 
  13.  
  14.   switch (who) { 
  15.  
  16.     case 'kaSong'
  17.  
  18.       resume with 230
  19.  
  20.     case 'xiaoMing'
  21.  
  22.       resume with 122
  23.  
  24.     default
  25.  
  26.       resume with 0
  27.  
  28.   } 
  29.  

當(dāng)執(zhí)行到getTotalPicNum內(nèi)部的getPicNum方法時(shí),會(huì)執(zhí)行perform name。

此時(shí)函數(shù)調(diào)用棧會(huì)從getPicNum方法內(nèi)跳出,被最近一個(gè)try...handle捕獲。類似throw Error后被最近一個(gè)try...catch捕獲。

類似throw Error后Error會(huì)作為catch的參數(shù),perform name后name會(huì)作為handle的參數(shù)。

與try...catch最大的不同在于:當(dāng)Error被catch捕獲后,之前的調(diào)用棧就銷毀了。而handle執(zhí)行resume后會(huì)回到之前perform的調(diào)用棧。

對(duì)于case 'kaSong',執(zhí)行完resume with 230;后調(diào)用棧會(huì)回到getPicNum,此時(shí)picNum === 230

再次申明,try...handle的語法是虛構(gòu)的,只是為了演示代數(shù)效應(yīng)的思想。

總結(jié)一下:代數(shù)效應(yīng)能夠?qū)⒏弊饔茫ɡ又袨檎?qǐng)求圖片數(shù)量)從函數(shù)邏輯中分離,使函數(shù)關(guān)注點(diǎn)保持純粹。

并且,從例子中可以看出,perform resume不需要區(qū)分同步異步。

代數(shù)效應(yīng)在React中的應(yīng)用

那么代數(shù)效應(yīng)與React有什么關(guān)系呢?最明顯的例子就是Hooks。

對(duì)于類似useState、useReducer、useRef這樣的Hook,我們不需要關(guān)注FunctionComponent的state在Hook中是如何保存的,React會(huì)為我們處理。

我們只需要假設(shè)useState返回的是我們想要的state,并編寫業(yè)務(wù)邏輯就行。

  1. function App() { 
  2.  
  3.   const [num, updateNum] = useState(0); 
  4.  
  5.    
  6.  
  7.   return ( 
  8.  
  9.     <button onClick={() => updateNum(num => num + 1)}>{num}</button>   
  10.  
  11.   ) 
  12.  

如果這個(gè)例子還不夠明顯,可以看看官方的Suspense Demo[2]

在Demo中ProfileDetails用于展示用戶名稱。而用戶名稱是異步請(qǐng)求的。

但是Demo中完全是同步的寫法。

  1. function ProfileDetails() { 
  2.  
  3.   const user = resource.user.read(); 
  4.  
  5.   return <h1>{user.name}</h1>; 
  6.  

代數(shù)效應(yīng)與Generator

從React15到React16,協(xié)調(diào)器(Reconciler)重構(gòu)的一大目的是:將老的同步更新的架構(gòu)變?yōu)楫惒娇芍袛喔隆?/p>

異步可中斷更新可以理解為:更新在執(zhí)行過程中可能會(huì)被打斷(瀏覽器時(shí)間分片用盡或有更高優(yōu)任務(wù)插隊(duì)),當(dāng)可以繼續(xù)執(zhí)行時(shí)恢復(fù)之前執(zhí)行的中間狀態(tài)。

這就是代數(shù)效應(yīng)中try...handle的作用。

其實(shí),瀏覽器原生就支持類似的實(shí)現(xiàn),這就是Generator。

但是Generator的一些缺陷使React團(tuán)隊(duì)放棄了他:

類似async,Generator也是傳染性的,使用了Generator則上下文的其他函數(shù)也需要作出改變。這樣心智負(fù)擔(dān)比較重。

Generator執(zhí)行的中間狀態(tài)是上下文關(guān)聯(lián)的。

考慮如下例子:

  1. function* doWork(A, B, C) { 
  2.  
  3.   var x = doExpensiveWorkA(A); 
  4.  
  5.   yield; 
  6.  
  7.   var y = x + doExpensiveWorkB(B); 
  8.  
  9.   yield; 
  10.  
  11.   var z = y + doExpensiveWorkC(C); 
  12.  
  13.   return z; 
  14.  

每當(dāng)瀏覽器有空閑時(shí)間都會(huì)依次執(zhí)行其中一個(gè)doExpensiveWork,當(dāng)時(shí)間用盡則會(huì)中斷,當(dāng)再次恢復(fù)時(shí)會(huì)從中斷位置繼續(xù)執(zhí)行。

只考慮“單一優(yōu)先級(jí)任務(wù)的中斷與繼續(xù)”情況下Generator可以很好的實(shí)現(xiàn)異步可中斷更新。

但是當(dāng)我們考慮“高優(yōu)先級(jí)任務(wù)插隊(duì)”的情況,如果此時(shí)已經(jīng)完成doExpensiveWorkA與doExpensiveWorkB計(jì)算出x與y。

此時(shí)B組件接收到一個(gè)高優(yōu)更新,由于Generator執(zhí)行的中間狀態(tài)是上下文關(guān)聯(lián)的的,所以重新計(jì)算y時(shí)無法復(fù)用之前已經(jīng)計(jì)算出的x,需要重新計(jì)算。

如果通過全局變量保存之前執(zhí)行的中間狀態(tài),又會(huì)引入新的復(fù)雜度。

更詳細(xì)的解釋可以參考這個(gè)issue[3]

基于這些原因,React沒有采用Generator實(shí)現(xiàn)協(xié)調(diào)器。

代數(shù)效應(yīng)與Fiber

Fiber并不是計(jì)算機(jī)術(shù)語中的新名詞,他的中文翻譯叫做纖程,與進(jìn)程(Process)、線程(Thread)、協(xié)程(Coroutine)同為程序執(zhí)行過程。

在很多文章中將纖程理解為協(xié)程的一種實(shí)現(xiàn)。在JS中,協(xié)程的實(shí)現(xiàn)便是Generator。

所以,我們可以將纖程(Fiber)、協(xié)程(Generator)理解為代數(shù)效應(yīng)思想在JS中的體現(xiàn)。

React Fiber可以理解為:

React內(nèi)部實(shí)現(xiàn)的一套狀態(tài)更新機(jī)制。支持任務(wù)不同優(yōu)先級(jí),可中斷與恢復(fù),并且恢復(fù)后可以復(fù)用之前的中間狀態(tài)。

其中每個(gè)任務(wù)更新單元為React Element對(duì)應(yīng)的Fiber節(jié)點(diǎn)。

 

責(zé)任編輯:張燕妮 來源: 程序員的那些事
相關(guān)推薦

2023-07-22 00:33:07

React團(tuán)隊(duì)數(shù)據(jù)

2021-05-24 06:00:20

ReactJSXJS

2020-01-07 15:40:43

React前端技術(shù)準(zhǔn)則

2023-02-02 08:41:14

React團(tuán)隊(duì)Vite

2023-03-24 12:34:56

2022-06-27 07:23:20

React?并發(fā)

2022-07-06 15:07:47

React開發(fā)

2020-11-24 07:48:32

React

2022-08-22 16:23:11

React特性

2022-05-06 07:31:01

useEventReactHook

2015-10-10 16:02:36

React NativAndroid

2022-10-29 08:55:19

頁面react

2023-05-31 07:29:46

2021-12-16 06:21:16

React組件前端

2022-04-15 08:07:21

ReactDiff算法

2021-10-15 14:28:30

React 組件渲染

2020-10-23 09:26:57

React-Redux

2021-02-02 08:11:50

火焰圖組件技術(shù)

2023-03-19 11:42:19

React新官方文檔

2023-07-03 07:51:47

點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)