日韩无码专区无码一级三级片|91人人爱网站中日韩无码电影|厨房大战丰满熟妇|AV高清无码在线免费观看|另类AV日韩少妇熟女|中文日本大黄一级黄色片|色情在线视频免费|亚洲成人特黄a片|黄片wwwav色图欧美|欧亚乱色一区二区三区

RELATEED CONSULTING
相關(guān)咨詢
選擇下列產(chǎn)品馬上在線溝通
服務(wù)時(shí)間:8:30-17:00
你可能遇到了下面的問(wèn)題
關(guān)閉右側(cè)工具欄

新聞中心

這里有您想知道的互聯(lián)網(wǎng)營(yíng)銷解決方案
React中setState是一個(gè)宏任務(wù)還是微任務(wù)?

最近有個(gè)朋友面試,面試官問(wèn)了個(gè)奇葩的問(wèn)題,也就是我寫在標(biāo)題上的這個(gè)問(wèn)題。

創(chuàng)新互聯(lián)成立10年來(lái),這條路我們正越走越好,積累了技術(shù)與客戶資源,形成了良好的口碑。為客戶提供網(wǎng)站設(shè)計(jì)、成都網(wǎng)站制作、網(wǎng)站策劃、網(wǎng)頁(yè)設(shè)計(jì)、域名與空間、網(wǎng)絡(luò)營(yíng)銷、VI設(shè)計(jì)、網(wǎng)站改版、漏洞修補(bǔ)等服務(wù)。網(wǎng)站是否美觀、功能強(qiáng)大、用戶體驗(yàn)好、性價(jià)比高、打開快等等,這些對(duì)于網(wǎng)站建設(shè)都非常重要,創(chuàng)新互聯(lián)通過(guò)對(duì)建站技術(shù)性的掌握、對(duì)創(chuàng)意設(shè)計(jì)的研究為客戶提供一站式互聯(lián)網(wǎng)解決方案,攜手廣大客戶,共同發(fā)展進(jìn)步。

能問(wèn)出這個(gè)問(wèn)題,面試官應(yīng)該對(duì) React 不是很了解,也是可能是看到面試者簡(jiǎn)歷里面有寫過(guò)自己熟悉 React,面試官想通過(guò)這個(gè)問(wèn)題來(lái)判斷面試者是不是真的熟悉 React ??。

面試官的問(wèn)法是否正確?

面試官的問(wèn)題是,setState 是一個(gè)宏認(rèn)為還是微任務(wù),那么在他的認(rèn)知里,setState 肯定是一個(gè)異步操作。為了判斷 setState 到底是不是異步操作,可以先做一個(gè)實(shí)驗(yàn),通過(guò) CRA 新建一個(gè) React 項(xiàng)目,在項(xiàng)目中,編輯如下代碼:

 
 
 
 
  1. import React from 'react'; 
  2. import logo from './logo.svg'; 
  3. import './App.css'; 
  4.  
  5. class App extends React.Component { 
  6.   state = { 
  7.     count: 1000 
  8.   } 
  9.   render() { 
  10.     return ( 
  11.        
  12.         
  13.           src={logo} alt="logo" 
  14.           className="App-logo" 
  15.           onClick={this.handleClick} 
  16.         /> 
  17.         

    我的關(guān)注人數(shù):{this.state.count}

     
  18.       
 
  •     ); 
  •   } 
  •  
  • export default App; 
  • 頁(yè)面大概長(zhǎng)這樣:

    上面的 React Logo 綁定了一個(gè)點(diǎn)擊事件,現(xiàn)在需要實(shí)現(xiàn)這個(gè)點(diǎn)擊事件,在點(diǎn)擊 Logo 之后,進(jìn)行一次 setState 操作,在 set 操作完成時(shí)打印一個(gè) log,并且在 set 操作之前,分別添加一個(gè)宏任務(wù)和微任務(wù)。代碼如下:

     
     
     
     
    1. handleClick = () => { 
    2.   const fans = Math.floor(Math.random() * 10) 
    3.   setTimeout(() => { 
    4.     console.log('宏任務(wù)觸發(fā)') 
    5.   }) 
    6.   Promise.resolve().then(() => { 
    7.     console.log('微任務(wù)觸發(fā)') 
    8.   }) 
    9.   this.setState({ 
    10.     count: this.state.count + fans 
    11.   }, () => { 
    12.     console.log('新增粉絲數(shù):', fans) 
    13.   }) 

    很明顯,在點(diǎn)擊 Logo 之后,先完成了 setState 操作,然后再是微任務(wù)的觸發(fā)和宏任務(wù)的觸發(fā)。所以,setState 的執(zhí)行時(shí)機(jī)是早于微任務(wù)與宏任務(wù)的,即使這樣也只能說(shuō)它的執(zhí)行時(shí)機(jī)早于 Promise.then,還不能證明它就是同步任務(wù)。

     
     
     
     
    1. handleClick = () => { 
    2.   const fans = Math.floor(Math.random() * 10) 
    3.   console.log('開始運(yùn)行') 
    4.   this.setState({ 
    5.     count: this.state.count + fans 
    6.   }, () => { 
    7.     console.log('新增粉絲數(shù):', fans) 
    8.   }) 
    9.   console.log('結(jié)束運(yùn)行') 

    這么看,似乎 setState 又是一個(gè)異步的操作。主要原因是,在 React 的生命周期以及綁定的事件流中,所有的 setState 操作會(huì)先緩存到一個(gè)隊(duì)列中,在整個(gè)事件結(jié)束后或者 mount 流程結(jié)束后,才會(huì)取出之前緩存的 setState 隊(duì)列進(jìn)行一次計(jì)算,觸發(fā) state 更新。只要我們跳出 React 的事件流或者生命周期,就能打破 React 對(duì) setState 的掌控。最簡(jiǎn)單的方法,就是把 setState 放到 setTimeout 的匿名函數(shù)中。

     
     
     
     
    1. handleClick = () => { 
    2.   setTimeout(() => { 
    3.     const fans = Math.floor(Math.random() * 10) 
    4.     console.log('開始運(yùn)行') 
    5.     this.setState({ 
    6.       count: this.state.count + fans 
    7.     }, () => { 
    8.       console.log('新增粉絲數(shù):', fans) 
    9.     }) 
    10.     console.log('結(jié)束運(yùn)行') 
    11.   }) 

    所以,setState 就是一次同步行為,根本不存在面試官的問(wèn)題。

    React 是如何控制 setState 的 ?

    前面的案例中,setState 只有在 setTimeout 中才會(huì)變得像一個(gè)同步方法,這是怎么做到的?

     
     
     
     
    1. handleClick = () => { 
    2.   // 正常的操作 
    3.   this.setState({ 
    4.     count: this.state.count + 1 
    5.   }) 
    6. handleClick = () => { 
    7.   // 脫離 React 控制的操作 
    8.   setTimeout(() => { 
    9.     this.setState({ 
    10.       count: this.state.count + fans 
    11.     }) 
    12.   }) 

    先回顧之前的代碼,在這兩個(gè)操作中,我們分別在 Performance 中記錄一次調(diào)用棧,看看兩者的調(diào)用棧有何區(qū)別。

    正常操作

    脫離 React 控制的操作

    在調(diào)用棧中,可以看到 Component.setState 方法最終會(huì)調(diào)用 enqueueSetState 方法,而 enqueueSetState 方法內(nèi)部會(huì)調(diào)用 scheduleUpdateOnFiber 方法,區(qū)別就在于正常調(diào)用的時(shí)候,scheduleUpdateOnFiber 方法內(nèi)只會(huì)調(diào)用 ensureRootIsScheduled ,在事件方法結(jié)束后,才會(huì)調(diào)用 flushSyncCallbackQueue 方法。而脫離 React 事件流的時(shí)候,scheduleUpdateOnFiber 在 ensureRootIsScheduled 調(diào)用結(jié)束后,會(huì)直接調(diào)用 flushSyncCallbackQueue 方法,這個(gè)方法就是用來(lái)更新 state 并重新進(jìn)行 render 。

     
     
     
     
    1. function scheduleUpdateOnFiber(fiber, lane, eventTime) { 
    2.   if (lane === SyncLane) { 
    3.     // 同步操作 
    4.     ensureRootIsScheduled(root, eventTime); 
    5.     // 判斷當(dāng)前是否還在 React 事件流中 
    6.     // 如果不在,直接調(diào)用 flushSyncCallbackQueue 更新 
    7.     if (executionContext === NoContext) { 
    8.       flushSyncCallbackQueue(); 
    9.     } 
    10.   } else { 
    11.     // 異步操作 
    12.   } 

    上述代碼可以簡(jiǎn)單描述這個(gè)過(guò)程,主要是判斷了 executionContext 是否等于 NoContext 來(lái)確定當(dāng)前更新流程是否在 React 事件流中。

    眾所周知,React 在綁定事件時(shí),會(huì)對(duì)事件進(jìn)行合成,統(tǒng)一綁定到 document 上( react@17 有所改變,變成了綁定事件到 render 時(shí)指定的那個(gè) DOM 元素),最后由 React 來(lái)派發(fā)。

    所有的事件在觸發(fā)的時(shí)候,都會(huì)先調(diào)用 batchedEventUpdates$1 這個(gè)方法,在這里就會(huì)修改 executionContext 的值,React 就知道此時(shí)的 setState 在自己的掌控中。

     
     
     
     
    1. // executionContext 的默認(rèn)狀態(tài) 
    2. var executionContext = NoContext; 
    3. function batchedEventUpdates$1(fn, a) { 
    4.   var prevExecutionContext = executionContext; 
    5.   executionContext |= EventContext; // 修改狀態(tài) 
    6.   try { 
    7.     return fn(a); 
    8.   } finally { 
    9.     executionContext = prevExecutionContext; 
    10.   // 調(diào)用結(jié)束后,調(diào)用 flushSyncCallbackQueue 
    11.     if (executionContext === NoContext) { 
    12.       flushSyncCallbackQueue(); 
    13.     } 
    14.   } 

    所以,不管是直接調(diào)用 flushSyncCallbackQueue ,還是推遲調(diào)用,這里本質(zhì)上都是同步的,只是有個(gè)先后順序的問(wèn)題。

    未來(lái)會(huì)有異步的 setState

    如果你有認(rèn)真看上面的代碼,你會(huì)發(fā)現(xiàn)在 scheduleUpdateOnFiber 方法內(nèi),會(huì)判斷 lane 是否為同步,那么是不是存在異步的情況?

     
     
     
     
    1. function scheduleUpdateOnFiber(fiber, lane, eventTime) { 
    2.   if (lane === SyncLane) { 
    3.     // 同步操作 
    4.     ensureRootIsScheduled(root, eventTime); 
    5.     // 判斷當(dāng)前是否還在 React 事件流中 
    6.     // 如果不在,直接調(diào)用 flushSyncCallbackQueue 更新 
    7.     if (executionContext === NoContext) { 
    8.       flushSyncCallbackQueue(); 
    9.     } 
    10.   } else { 
    11.     // 異步操作 
    12.   } 

    React 在兩年前,升級(jí) fiber 架構(gòu)的時(shí)候,就是為其異步化做準(zhǔn)備的。在 React 18 將會(huì)正式發(fā)布 Concurrent 模式,關(guān)于 Concurrent 模式,官方的介紹如下。

    什么是 Concurrent 模式?

    Concurrent 模式是一組 React 的新功能,可幫助應(yīng)用保持響應(yīng),并根據(jù)用戶的設(shè)備性能和網(wǎng)速進(jìn)行適當(dāng)?shù)恼{(diào)整。在 Concurrent 模式中,渲染不是阻塞的。它是可中斷的。這改善了用戶體驗(yàn)。它同時(shí)解鎖了以前不可能的新功能。

    現(xiàn)在如果想使用 Concurrent 模式,需要使用 React 的實(shí)驗(yàn)版本。

    本文轉(zhuǎn)載自微信公眾號(hào)「自然醒的筆記本」,可以通過(guò)以下二維碼關(guān)注。轉(zhuǎn)載本文請(qǐng)聯(lián)系自然醒的筆記本公眾號(hào)。


    網(wǎng)站名稱:React中setState是一個(gè)宏任務(wù)還是微任務(wù)?
    標(biāo)題鏈接:http://m.5511xx.com/article/ccsidds.html