<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

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

          共 3696字,需瀏覽 8分鐘

           ·

          2020-08-12 03:54

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

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

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

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

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

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

          function?getTotalPicNum(user1,?user2)?{
          ??const?num1?=?getPicNum(user1);
          ??const?num2?=?getPicNum(user2);

          ??return?picNum1?+?picNum2;
          }

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

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

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

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

          async?function?getTotalPicNum(user1,?user2)?{
          ??const?num1?=?await?getPicNum(user1);
          ??const?num2?=?await?getPicNum(user2);

          ??return?picNum1?+?picNum2;
          }

          但是,async await是有傳染性的 —— 當(dāng)一個函數(shù)變?yōu)?code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(30, 107, 184);background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;">async后,這意味著調(diào)用他的函數(shù)也需要是async,這破壞了getTotalPicNum的同步特性。

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

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

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

          function?getPicNum(name)?{
          ??const?picNum?=?perform?name;
          ??return?picNum;
          }

          try?{
          ??getTotalPicNum('kaSong',?'xiaoMing');
          }?handle?(who)?{
          ??switch?(who)?{
          ????case?'kaSong':
          ??????resume?with?230;
          ????case?'xiaoMing':
          ??????resume?with?122;
          ????default:
          ??????resume?with?0;
          ??}
          }

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

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

          類似throw ErrorError會作為catch的參數(shù),perform namename會作為handle的參數(shù)。

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

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

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

          總結(jié)一下:代數(shù)效應(yīng)能夠?qū)?code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(30, 107, 184);background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;">副作用(例子中為請求圖片數(shù)量)從函數(shù)邏輯中分離,使函數(shù)關(guān)注點(diǎn)保持純粹。

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

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

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

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

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

          function?App()?{
          ??const?[num,?updateNum]?=?useState(0);
          ??
          ??return?(
          ????<button?onClick={()?=>?updateNum(num?=>?num?+?1)}>{num}button>??
          ??)
          }

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

          DemoProfileDetails用于展示用戶名稱。而用戶名稱異步請求的。

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

          function?ProfileDetails()?{
          ??const?user?=?resource.user.read();
          ??return?<h1>{user.name}h1>;
          }

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

          React15React16,協(xié)調(diào)器(Reconciler)重構(gòu)的一大目的是:將老的同步更新的架構(gòu)變?yōu)?code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(30, 107, 184);background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;">異步可中斷更新。

          異步可中斷更新可以理解為:更新在執(zhí)行過程中可能會被打斷(瀏覽器時間分片用盡或有更高優(yōu)任務(wù)插隊(duì)),當(dāng)可以繼續(xù)執(zhí)行時恢復(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)的。

          考慮如下例子:

          function*?doWork(A,?B,?C)?{
          ??var?x?=?doExpensiveWorkA(A);
          ??yield;
          ??var?y?=?x?+?doExpensiveWorkB(B);
          ??yield;
          ??var?z?=?y?+?doExpensiveWorkC(C);
          ??return?z;
          }

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

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

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

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

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

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

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

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

          Fiber并不是計算機(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)先級,可中斷與恢復(fù),并且恢復(fù)后可以復(fù)用之前的中間狀態(tài)。

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

          參考資料

          [1]

          Sebastian Markb?ge: https://github.com/sebmarkbage/

          [2]

          Suspense Demo: https://codesandbox.io/s/frosty-hermann-bztrp?file=/src/index.js:152-160

          [3]

          這個issue: https://github.com/facebook/react/issues/7942#issuecomment-254987818

          ?? 看完三件事

          如果你覺得這篇內(nèi)容對你挺有啟發(fā),我想邀請你幫我三個小忙:


          1. 點(diǎn)個「在看」,讓更多的人也能看到這篇內(nèi)容(喜歡不點(diǎn)在看,都是耍流氓 -_-)

          2. 關(guān)注我的官網(wǎng)?https://muyiy.cn,讓我們成為長期關(guān)系

          3. 關(guān)注公眾號「高級前端進(jìn)階」,公眾號后臺回復(fù)「面試題」 送你高級前端面試題,回復(fù)「加群」加入面試互助交流群


          》》面試官都在用的題庫,快來看看《《

          瀏覽 16
          點(diǎn)贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點(diǎn)贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  一区二区视频传媒 | 亚洲欧洲视频 | www欧美区 | 精品人妻人伦 | 嗯~啊~乖~进去了~h~乖视频网站免费 |