亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

詳細談?wù)凴eact中setState是一個宏任務(wù)還是微任務(wù)

 更新時間:2021年09月08日 11:34:39   作者:自然醒的博客  
學過react的人都知道,setState在react里是一個很重要的方法,使用它可以更新我們數(shù)據(jù)的狀態(tài),下面這篇文章主要給大家介紹了關(guān)于React中setState是一個宏任務(wù)還是微任務(wù)的相關(guān)資料,需要的朋友可以參考下

前言

最近有個朋友面試,面試官問了個奇葩的問題,也就是我寫在標題上的這個問題。

能問出這個問題,面試官應(yīng)該對 React 不是很了解,也是可能是看到面試者簡歷里面有寫過自己熟悉 React,面試官想通過這個問題來判斷面試者是不是真的熟悉 React 🤣。

面試官的問法是否正確?§

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

import React from 'react';
import logo from './logo.svg';
import './App.css';

class App extends React.Component {
  state = {
    count: 1000
  }
  render() {
    return (
      <div className="App">
        <img
          src={logo} alt="logo"
          className="App-logo"
          onClick={this.handleClick}
        />
        <p>我的關(guān)注人數(shù):{this.state.count}</p>
      </div>
    );
  }
}

export default App;

頁面大概長這樣:

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

handleClick = () => {
  const fans = Math.floor(Math.random() * 10)
  setTimeout(() => {
    console.log('宏任務(wù)觸發(fā)')
  })
  Promise.resolve().then(() => {
    console.log('微任務(wù)觸發(fā)')
  })
  this.setState({
    count: this.state.count + fans
  }, () => {
    console.log('新增粉絲數(shù):', fans)
  })
}

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

handleClick = () => {
  const fans = Math.floor(Math.random() * 10)
  console.log('開始運行')
  this.setState({
    count: this.state.count + fans
  }, () => {
    console.log('新增粉絲數(shù):', fans)
  })
  console.log('結(jié)束運行')
}

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

handleClick = () => {
  setTimeout(() => {
    const fans = Math.floor(Math.random() * 10)
    console.log('開始運行')
    this.setState({
      count: this.state.count + fans
    }, () => {
      console.log('新增粉絲數(shù):', fans)
    })
    console.log('結(jié)束運行')
  })
}

由此可見,setState 本質(zhì)上還是在一個事件循環(huán)中,并沒有切換到另外宏任務(wù)或者微任務(wù)中,在運行上是基于同步代碼實現(xiàn),只是行為上看起來像異步。所以,根本不存在面試官的問題。

React 是如何控制 setState 的 ?§

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

handleClick = () => {
  // 正常的操作
  this.setState({
    count: this.state.count + 1
  })
}
handleClick = () => {
  // 脫離 React 控制的操作
  setTimeout(() => {
    this.setState({
      count: this.state.count + fans
    })
  })
}

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

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

function scheduleUpdateOnFiber(fiber, lane, eventTime) {
  if (lane === SyncLane) {
    // 同步操作
    ensureRootIsScheduled(root, eventTime);
    // 判斷當前是否還在 React 事件流中
    // 如果不在,直接調(diào)用 flushSyncCallbackQueue 更新
    if (executionContext === NoContext) {
      flushSyncCallbackQueue();
    }
  } else {
    // 異步操作
  }
}

上述代碼可以簡單描述這個過程,主要是判斷了 executionContext 是否等于 NoContext 來確定當前更新流程是否在 React 事件流中。

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

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

// executionContext 的默認狀態(tài)
var executionContext = NoContext;
function batchedEventUpdates$1(fn, a) {
  var prevExecutionContext = executionContext;
  executionContext |= EventContext; // 修改狀態(tài)
  try {
    return fn(a);
  } finally {
    executionContext = prevExecutionContext;
    // 調(diào)用結(jié)束后,調(diào)用 flushSyncCallbackQueue
    if (executionContext === NoContext) {
      flushSyncCallbackQueue();
    }
  }
}

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

未來會有異步的 setState§

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

function scheduleUpdateOnFiber(fiber, lane, eventTime) {
  if (lane === SyncLane) {
    // 同步操作
    ensureRootIsScheduled(root, eventTime);
    // 判斷當前是否還在 React 事件流中
    // 如果不在,直接調(diào)用 flushSyncCallbackQueue 更新
    if (executionContext === NoContext) {
      flushSyncCallbackQueue();
    }
  } else {
    // 異步操作
  }
}

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

什么是 Concurrent 模式?

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

現(xiàn)在如果想使用 Concurrent 模式,需要使用 React 的實驗版本。如果你對這部分內(nèi)容感興趣可以閱讀我之前的文章:https://blog.shenfq.com/posts/2020/React%20架構(gòu)的演變%20-%20從同步到異步.html

總結(jié)

到此這篇關(guān)于React中setState是一個宏任務(wù)還是微任務(wù)的文章就介紹到這了,更多相關(guān)React setState是宏任務(wù)還是微任務(wù)內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • React踩坑之a(chǎn)ntd輸入框rules中的required=true問題

    React踩坑之a(chǎn)ntd輸入框rules中的required=true問題

    這篇文章主要介紹了React踩坑之a(chǎn)ntd輸入框rules中的required=true問題,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2023-06-06
  • React函數(shù)式組件Hook中的useEffect函數(shù)的詳細解析

    React函數(shù)式組件Hook中的useEffect函數(shù)的詳細解析

    useEffect是react v16.8新引入的特性。我們可以把useEffect hook看作是componentDidMount、componentDidUpdate、componentWillUnmounrt三個函數(shù)的組合
    2022-10-10
  • 從零開始搭建一個react項目開發(fā)

    從零開始搭建一個react項目開發(fā)

    這篇文章主要介紹了從零開始搭建一個react項目開發(fā),小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2018-02-02
  • React報錯信息之Expected?an?assignment?or?function?call?and?instead?saw?an?expression

    React報錯信息之Expected?an?assignment?or?function?call?and?

    這篇文章主要介紹了React報錯之Expected?an?assignment?or?function?call?and?instead?saw?an?expression,下面有兩個示例來展示錯誤是如何產(chǎn)生的,需要的朋友可以參考下
    2022-08-08
  • react中的forwardRef 和memo的區(qū)別解析

    react中的forwardRef 和memo的區(qū)別解析

    forwardRef和memo是React中用于性能優(yōu)化和組件復用的兩個高階函數(shù),本文給大家介紹react中的forwardRef 和memo的區(qū)別及適用場景,感興趣的朋友跟隨小編一起看看吧
    2023-10-10
  • React Component存在的幾種形式詳解

    React Component存在的幾種形式詳解

    這篇文章主要給大家介紹了關(guān)于React Component存在的幾種形式,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2018-11-11
  • 深入了解React中的合成事件

    深入了解React中的合成事件

    React 中的事件,是對原生事件的封裝,叫做合成事件。這篇文章主要通過幾個簡單的示例為大家詳細介紹一下React中的合成事件,感興趣的可以了解一下
    2023-02-02
  • create-react-app全家桶router?mobx全局安裝配置

    create-react-app全家桶router?mobx全局安裝配置

    這篇文章主要為大家介紹了create-react-app全家桶router?mobx全局安裝配置,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2022-06-06
  • React Native使用Modal自定義分享界面的示例代碼

    React Native使用Modal自定義分享界面的示例代碼

    本篇文章主要介紹了React Native使用Modal自定義分享界面的示例代碼,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2017-10-10
  • React項目中不需要jQuery原因分析

    React項目中不需要jQuery原因分析

    在Web開發(fā)的早期,jQuery是一個革命性的庫,它極大地簡化了DOM操作、事件處理、動畫制作以及Ajax請求等任務(wù),React的出現(xiàn),jQuery在新項目中的必要性開始受到質(zhì)疑,本文將探討為什么在React應(yīng)用中不需要jQuery,感興趣的朋友可以參考下
    2024-02-02

最新評論