成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

React 并發(fā)模式到底是個(gè)啥?

開(kāi)發(fā) 前端
到目前為止,React 的并發(fā)模式就只體現(xiàn)在任務(wù)優(yōu)先級(jí)和任務(wù)可被中斷上。如果單獨(dú)考慮任務(wù)可被中斷,他實(shí)現(xiàn)的效果就跟防抖、節(jié)流比較類(lèi)似,概念比較高大上,但說(shuō)穿了其實(shí)也沒(méi)啥用。

在計(jì)算機(jī)里,并發(fā)「concurrent」一詞,最早是用來(lái)表示多個(gè)任務(wù)同時(shí)進(jìn)行。但是由于早期的計(jì)算機(jī)能力有限,單核計(jì)算機(jī)同一時(shí)間,只能運(yùn)行一個(gè)任務(wù)。因此,為了做到看上去多個(gè)應(yīng)用是在同時(shí)運(yùn)行的,單核計(jì)算機(jī)就快速的在不同的應(yīng)用中來(lái)回切換,它執(zhí)行完 A 應(yīng)用的一個(gè)任務(wù),就執(zhí)行 B 應(yīng)用的任務(wù),只要切換得足夠快,對(duì)于用戶(hù)而言,A 應(yīng)用與 B 應(yīng)用就是在同時(shí)運(yùn)行。

因此,對(duì)于單核 CPU 來(lái)說(shuō),多個(gè)任務(wù)同時(shí)執(zhí)行這種情況并不存在。

后來(lái)的主流計(jì)算機(jī)已經(jīng)可以做到多個(gè)任務(wù)同時(shí)執(zhí)行了,但是并發(fā)一詞已經(jīng)有了自己專(zhuān)屬的場(chǎng)景,于是我們把真正的多個(gè)任務(wù)同時(shí)執(zhí)行又重新取了一個(gè)名字,并行「parallel」

而并發(fā)則保留了它原本在單核 CPU 上的的含義:多個(gè)任務(wù)切換執(zhí)行。為了知道下一個(gè)任務(wù)到底應(yīng)該是誰(shuí)執(zhí)行了,那么單核 CPU 上必定會(huì)設(shè)計(jì)一個(gè)調(diào)度模式,用來(lái)確定任務(wù)的優(yōu)先級(jí)。因此,并發(fā)的另外一個(gè)角度的解讀,就是多個(gè)任務(wù)對(duì)同一執(zhí)行資源的競(jìng)爭(zhēng)。

一、React 的并發(fā)

在頁(yè)面使用 JS 操作 DOM 渲染頁(yè)面的過(guò)程中,也是同樣的道理,他不存在有兩個(gè)任務(wù)能同時(shí)執(zhí)行的情況。不過(guò),React 設(shè)計(jì)了一種機(jī)制,來(lái)模擬渲染資源的競(jìng)爭(zhēng)。

首先,React 設(shè)計(jì)了一個(gè)調(diào)度器,Scheduler,來(lái)調(diào)度任務(wù)的優(yōu)先級(jí)。

但是在爭(zhēng)取誰(shuí)更先渲染這個(gè)事情,在瀏覽器的渲染原理里,他經(jīng)不起推敲。為什么呢?因?yàn)闉g覽器的底層渲染機(jī)制有收集邏輯,他會(huì)合并所有的渲染指令

div.style.color = 'red'
div.style.backgroundColor = '#FFF'
...

多個(gè)指令,會(huì)被合并成一個(gè)渲染任務(wù)。那也就意味著,對(duì)于瀏覽器而言,不存在渲染資源的競(jìng)爭(zhēng),因?yàn)椴煌匿秩局噶疃紩?huì)被合并。既然這樣,那 React 的并發(fā)又是怎么回事呢?

還有更詭異的事情,React 的渲染指令,是通過(guò) setState 來(lái)觸發(fā),我們知道,多個(gè) setState 指令,React 也會(huì)將他們合并批處理

setLoading(false)
setList([])

// 等價(jià)于
setState({
  loading: false,
  list: []
})

既然如此,并發(fā)體現(xiàn)在什么地方呢?也不存在渲染資源的競(jìng)爭(zhēng)啊?我們看不到任務(wù)的切換執(zhí)行,也看不到不同任務(wù)對(duì)渲染資源的競(jìng)爭(zhēng)。所以真相就是...

大多數(shù)情況下,React 確實(shí)并不存在任何并發(fā)現(xiàn)象。

而事實(shí)上,當(dāng)我們已經(jīng)明確了哪些 DOM 需要被操作,對(duì)于瀏覽器來(lái)說(shuō),他可以足夠快的渲染更新,因此,在一幀的時(shí)間里,就算合并非常多的 DOM 操作,瀏覽器也足以應(yīng)對(duì)。夠用,就表示競(jìng)爭(zhēng)毫無(wú)意義。

只有在渲染超大量的 DOM 和大量表單時(shí),瀏覽器的渲染引擎表示有壓力

因此,資源競(jìng)爭(zhēng)只會(huì)發(fā)生在,渲染能力不夠用的時(shí)候。

一次渲染包括兩個(gè)部分,一個(gè)部分是 JS 邏輯,我們需要在 JS 邏輯中明確具體的 DOM 操作是什么。第二個(gè)部分是渲染引擎執(zhí)行渲染任務(wù)。很明顯,對(duì)于 React 而言,他無(wú)法改變渲染引擎的邏輯。那么也就意味著,React 的并發(fā)只會(huì)發(fā)生在第一個(gè)部分:JS 邏輯中。

因此,react 還設(shè)計(jì)了第二步驟,Reconciler。當(dāng)我們通過(guò) setState 觸發(fā)一個(gè)渲染任務(wù)時(shí),react 需要在 Reconciler 中,利用 diff 算法找出來(lái)哪些 DOM 需要被更改。如果多個(gè) setState 指令合并之后,我們發(fā)現(xiàn) diff 過(guò)程超出了一幀的時(shí)間,這個(gè)時(shí)候就有可能會(huì)存在渲染資源的競(jìng)爭(zhēng)。

Scheduler

Reconciler

Renderer

收集

diff

操作 DOM

優(yōu)先級(jí)

可中斷


但是,如果只有一幀超出的時(shí)候,這一幀之后,瀏覽器再也沒(méi)有新的渲染任務(wù),那么就算超出了也無(wú)所謂。也沒(méi)有必要去競(jìng)爭(zhēng)渲染資源,只有一種可能,那就是短時(shí)間之內(nèi)需要多次渲染。如果每一幀的時(shí)間都超標(biāo)了,那么頁(yè)面就會(huì)卡頓。

因此,只有在短時(shí)間之內(nèi)頁(yè)面需要多次渲染,才會(huì)存在資源競(jìng)爭(zhēng)的情況。這個(gè)時(shí)候我們才會(huì)考慮并發(fā)的存在。

我們還需要進(jìn)一步思考。剛才我們已經(jīng)分析出,只有在短時(shí)間之內(nèi)多次渲染,并且造成了頁(yè)面卡頓,我們才會(huì)考慮并發(fā)。說(shuō)明此時(shí)我們想要使用并發(fā)來(lái)解決的問(wèn)題就是讓頁(yè)面不卡頓。因此,在多次渲染的前提下,多個(gè)任務(wù)的競(jìng)爭(zhēng)結(jié)果就一定是渲染任務(wù)總量減少了,才會(huì)不卡頓。所以我們要做的事情就是,找出優(yōu)先級(jí)更低的任務(wù),即使他掉幀,只要不影響頁(yè)面卡頓,我們都可以接受。

在 React 的底層設(shè)計(jì)中,setState 是一個(gè)任務(wù),但是這個(gè)任務(wù)會(huì)影響哪些 UI 發(fā)生變化,它就可能會(huì)對(duì)應(yīng)多個(gè) Fiber,每一個(gè) Fiber 的執(zhí)行都是一個(gè)小任務(wù),我們可以把一個(gè)任務(wù)看成一個(gè)函數(shù)。

一旦一個(gè)任務(wù)開(kāi)始執(zhí)行之后,React 不具備提前判斷這個(gè)任務(wù)執(zhí)行結(jié)束需要多少時(shí)間。只有等他執(zhí)行完了,我們才能夠算出來(lái)他一共執(zhí)行了多久。因此,對(duì)于哪些 setState 是耗時(shí)較長(zhǎng)的任務(wù),React 無(wú)法判斷,只有通過(guò)開(kāi)發(fā)者自己去判斷。我們需要在觸發(fā) setState 時(shí),就標(biāo)記這個(gè)任務(wù)的優(yōu)先級(jí),否則 react 也判斷不了這個(gè)任務(wù)是否耗時(shí)比較長(zhǎng)。因此,我們需要手動(dòng)使用 startTransition 來(lái)標(biāo)記耗時(shí)的 setState

function TabContainer() {
  const [isPending, startTransition] = useTransition();
  const [tab, setTab] = useState('about');

  function selectTab(nextTab) {
    startTransition(() => {
      setTab(nextTab);
    });
  }
  // ……
}

另外一個(gè)問(wèn)題就是,競(jìng)爭(zhēng)是如何發(fā)生的。

通過(guò)時(shí)間切片中斷任務(wù)的執(zhí)行,給優(yōu)先級(jí)更高的任務(wù)一個(gè)插隊(duì)的機(jī)會(huì)。

例如上面例子,當(dāng)我們使用 StartTransition 標(biāo)記了 setTab 為一個(gè)耗時(shí)較長(zhǎng)的任務(wù)時(shí)。setTab 會(huì)有許多小的 Fiber 節(jié)點(diǎn)任務(wù)組成,我們?cè)?Reconciler 階段執(zhí)行每一個(gè)小的 Fiber 節(jié)點(diǎn)任務(wù)之前,都會(huì)判斷此時(shí)是否應(yīng)該打斷循環(huán)。

function workLoop(hasTimeRemaining, initialTime) {
  var currentTime = initialTime;
  advanceTimers(currentTime);
  currentTask = peek(taskQueue);

  while (currentTask !== null && !(enableSchedulerDebugging )) {
    if (currentTask.expirationTime > currentTime && (!hasTimeRemaining || shouldYieldToHost())) {
      // 當(dāng)前任務(wù)尚未過(guò)期,但時(shí)間已經(jīng)到了最后期限
      break;
    }

這里的 frameInterval 的具體值為 5ms,就是一個(gè)時(shí)間分片。也就是說(shuō),在 子 Fiber 任務(wù)執(zhí)行的遍歷過(guò)程中,每大于 5ms,就會(huì)被打斷一次。這樣才有給更高優(yōu)先級(jí)任務(wù)執(zhí)行的機(jī)會(huì)。

function shouldYieldToHost() {
  var timeElapsed = getCurrentTime() - startTime;

  if (timeElapsed < frameInterval) { // 5ms
    // 主線(xiàn)程只被阻塞了很短時(shí)間;
    // smaller than a single frame. Don't yield yet.
    return false;
  } 
  // 主線(xiàn)程被阻塞的時(shí)間不可忽視
  return true;
}

這里需要注意的是,setTab 最終被中斷,是由于時(shí)間分片之內(nèi)沒(méi)有足夠的時(shí)間給他執(zhí)行每一個(gè) Fiber 節(jié)點(diǎn)任務(wù),而并非是由更高優(yōu)先級(jí)的任務(wù)產(chǎn)生了導(dǎo)致它的中斷。優(yōu)先級(jí)只會(huì)影響隊(duì)列的排序結(jié)果。

例如,假設(shè) setTab 影響的 UI 中包含一個(gè)父級(jí) Fiber 節(jié)點(diǎn)和 250 個(gè)子級(jí)Fiber 節(jié)點(diǎn)。如果我們對(duì)子 Fiber 節(jié)點(diǎn)增加一個(gè) 1ms 的阻塞,此時(shí)就至少有 50 個(gè)中斷間隔給優(yōu)先級(jí)更高的任務(wù)執(zhí)行。

function Item(props: { text: string }) {
  let startTime = performance.now();
  while (performance.now() - startTime < 1) {}
  console.log('text')
  return (
    <div>{props.text}</div>
  )
}

因此,在真實(shí)的渲染邏輯中,如果我的設(shè)備足夠強(qiáng)悍,執(zhí)行速度足夠快,就算是我標(biāo)記了低優(yōu)先級(jí),也可能不會(huì)被中斷。

這里還需要注意的是,任務(wù)的最小單位是 Fiber,如果你的單個(gè) Fiber 執(zhí)行時(shí)間過(guò)長(zhǎng),react 也無(wú)法拆分這個(gè)任務(wù)。這種情況下,我們應(yīng)該想辦法把執(zhí)行壓力分散到子組件中去。

二、總結(jié)

到目前為止,React 的并發(fā)模式就只體現(xiàn)在任務(wù)優(yōu)先級(jí)和任務(wù)可被中斷上。如果單獨(dú)考慮任務(wù)可被中斷,他實(shí)現(xiàn)的效果就跟防抖、節(jié)流比較類(lèi)似,概念比較高大上,但說(shuō)穿了其實(shí)也沒(méi)啥用。如果你不用 useTransition/useDefferedValue 的話(huà),基本上你的任務(wù)也不會(huì)被中斷。

但是如果不考慮任務(wù)可被中斷呢,優(yōu)先級(jí)隊(duì)列其實(shí)也沒(méi)啥太大的意義。所以 react 的并發(fā)模式,從我個(gè)人主觀(guān)的角度來(lái)看的話(huà),宣傳意義大于實(shí)際意義。

責(zé)任編輯:姜華 來(lái)源: 這波能反殺
相關(guān)推薦

2022-05-04 08:38:32

Netty網(wǎng)絡(luò)框架

2021-05-11 07:30:58

JNIJavaAPI

2021-01-28 17:41:32

Github網(wǎng)站Pull Reques

2022-04-10 19:26:07

TypeScript類(lèi)型語(yǔ)法

2024-07-12 15:08:23

Python@wraps函數(shù)

2021-12-26 00:01:51

Log4Shell漏洞服務(wù)器

2024-08-26 14:23:56

2022-09-06 21:38:45

數(shù)字人數(shù)字孿生

2021-12-16 15:11:59

Facebook天秤幣加密貨幣

2024-08-01 17:34:56

Promiseaxios請(qǐng)求

2012-07-25 09:09:46

GNOME OS桌面

2013-05-29 10:17:56

Hadoop分布式文件系統(tǒng)

2024-02-26 00:00:00

人工智能序列數(shù)據(jù)機(jī)器人

2020-03-07 09:47:48

AVL樹(shù)算法場(chǎng)景

2020-10-29 07:03:56

Docker容器存儲(chǔ)

2024-02-01 20:15:37

2021-12-16 21:13:38

通信網(wǎng)管平臺(tái)

2025-05-28 00:30:00

MCP智能體Agent

2019-10-28 09:59:26

區(qū)塊鏈技術(shù)智能

2021-09-13 13:24:22

硬盤(pán)SLC緩存技術(shù)SSD
點(diǎn)贊
收藏

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

主站蜘蛛池模板: 久久久久久久亚洲精品 | 一区二区在线不卡 | 欧美一级免费观看 | 色天堂影院 | 日韩成人 | 久久这里只有精品首页 | 久久久久久久久久一区二区 | 中文字幕在线观看成人 | 久久综合久久自在自线精品自 | 国产精品精品视频一区二区三区 | 欧美激情一区二区三区 | 久久久91精品国产一区二区三区 | 精品美女视频在线观看免费软件 | 颜色网站在线观看 | m豆传媒在线链接观看 | 精品三级在线观看 | 特级毛片www| 韩日在线视频 | 男女网站视频 | 欧美白人做受xxxx视频 | 狠狠av| 人成在线| 91精品久久久 | 黄色片网站国产 | 精品99爱视频在线观看 | 成人性视频在线 | 久久精品中文字幕 | 91成人 | 久久毛片网站 | 午夜视频在线观看一区二区 | 久久久久久久久国产成人免费 | 东京av男人的天堂 | 精品国产乱码一区二区三区a | 日本啊v在线 | 亚洲乱码一区二区三区在线观看 | 精品国产乱码久久久 | 亚洲高清在线观看 | 人人人艹 | 国产乱码精品一区二区三区中文 | 奇米超碰在线 | 欧美精品一区二区三区蜜桃视频 |