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

如何用 setTimeout(fn, 0) 來降級任務(wù)優(yōu)先級?

開發(fā)
setTimeout(fn, 0) 是一個看似簡單卻蘊含深刻原理的技巧。它不是用來精確計時的,而是一種利用事件循環(huán)機制,將任務(wù)從同步執(zhí)行流中剝離,推入異步任務(wù)隊列的手段。

在 JavaScript 開發(fā)中,我們經(jīng)常會遇到這樣的場景:一個計算量很大的任務(wù)阻塞了主線程,導(dǎo)致頁面卡頓,用戶交互無響應(yīng)。為了解決這個問題,開發(fā)者們探索了各種方法,其中一個看似奇怪卻非常有效的技巧就是 setTimeout(fn, 0)。

setTimeout(fn, 0) 的字面意思是“在0毫秒后執(zhí)行函數(shù) fn”。實際上,它并不會立即執(zhí)行,這行代碼是一種巧妙的“障眼法”,它利用了 JavaScript 的事件循環(huán)機制,將一個任務(wù)的優(yōu)先級“降級”,從而為更高優(yōu)先級的任務(wù)讓路。

“0毫秒延遲”的真相

首先,我們必須明確一點:setTimeout(fn, 0) 并不意味著函數(shù)會立即在0毫秒后執(zhí)行。

這里的 0 實際上是瀏覽器能接受的最小延遲時間。根據(jù) HTML5 標(biāo)準(zhǔn),setTimeout 的第二個參數(shù)如果小于4ms,可能會被瀏覽器默認為4ms。但更重要的是,無論延遲時間是0還是4,setTimeout 的核心作用是將函數(shù) fn 放入一個異步的“任務(wù)隊列”中,等待未來的某個時刻執(zhí)行。

這個“未來的某個時刻”是何時呢?答案就在 JavaScript 的心臟——**事件循環(huán)(Event Loop)**中。

核心機制:事件循環(huán)(Event Loop)

要理解 setTimeout 的魔力,我們必須先理解 JavaScript 是如何處理任務(wù)的。

想象一個餐廳的廚房:

  • 調(diào)用棧(Call Stack):這是主廚當(dāng)前正在處理的菜品清單。JavaScript 是單線程的,就像一位主廚,一次只能專注做一道菜。所有同步代碼都會被依次放入調(diào)用棧中執(zhí)行。
  • Web APIs(瀏覽器 API):這是廚房里的其他幫手,比如定時器(setTimeout)、負責(zé)網(wǎng)絡(luò)請求的服務(wù)員(AJAX)、處理客人動作的迎賓(DOM事件)。當(dāng)主廚(調(diào)用棧)遇到 setTimeout 這樣的指令時,他不會自己計時,而是把它交給定時器幫手,然后繼續(xù)做下一道菜。
  • 任務(wù)隊列(Task Queue / Callback Queue):這是幫手們完成任務(wù)后,將需要主廚處理的后續(xù)工作(即回調(diào)函數(shù))放置的等候區(qū)。比如,定時器時間到了,就會把 fn 這個回調(diào)函數(shù)放到這個隊列里排隊。

事件循環(huán)(Event Loop) 就是廚房里的調(diào)度員。它會不斷地檢查:“主廚(調(diào)用棧)手里的活都干完了嗎?”

  • 如果調(diào)用棧不為空,調(diào)度員就耐心等待。
  • 一旦調(diào)用棧為空(所有同步代碼都執(zhí)行完畢),調(diào)度員就會去任務(wù)隊列里看看有沒有等待處理的任務(wù)。
  • 如果有,它會取出隊列中的第一個任務(wù),并將其放入調(diào)用棧中,讓主廚開始處理。

這個過程周而復(fù)始,構(gòu)成了 JavaScript 的并發(fā)模型。

現(xiàn)在,我們用一個經(jīng)典的例子來演示這個流程:

console.log('A: 同步任務(wù)開始');

setTimeout(() => {
  console.log('C: 異步任務(wù)執(zhí)行');
}, 0);

console.log('B: 同步任務(wù)結(jié)束');

// 輸出結(jié)果是什么?
// A: 同步任務(wù)開始
// B: 同步任務(wù)結(jié)束
// C: 異步任務(wù)執(zhí)行

執(zhí)行步驟拆解:

  • console.log('A') 進入調(diào)用棧,執(zhí)行,打印 “A”,然后出棧。
  • setTimeout 進入調(diào)用棧。它不是一個耗時操作,它的作用是通知瀏覽器 Web APIs 里的“定時器”:“嘿,請在0毫秒后將這個回調(diào)函數(shù) () => { console.log('C') } 放到任務(wù)隊列里去。” 說完,setTimeout 自己就執(zhí)行完畢并出棧了。
  • console.log('B') 進入調(diào)用棧,執(zhí)行,打印 “B”,然后出棧。
  • 此時,所有同步代碼都執(zhí)行完畢,調(diào)用棧變空。
  • 與此同時(或者說幾乎是同時),定時器幫手發(fā)現(xiàn)0毫秒已經(jīng)到了,于是將 C 的回調(diào)函數(shù)放入了任務(wù)隊列。
  • 事件循環(huán)發(fā)現(xiàn)調(diào)用棧是空的,于是去任務(wù)隊列里檢查,發(fā)現(xiàn)了 C 的回調(diào)函數(shù)。
  • 事件循環(huán)將 C 的回調(diào)函數(shù)推入調(diào)用棧。
  • 調(diào)用棧執(zhí)行 C 的回調(diào)函數(shù),打印 “C”,然后出棧。

如何實現(xiàn)“降級”任務(wù)優(yōu)先級?

現(xiàn)在我們回到最初的問題。setTimeout(fn, 0) 到底是如何“降級”任務(wù)優(yōu)先級的?

所謂的“降級”,就是將任務(wù)從“立即執(zhí)行的同步代碼”變?yōu)椤靶枰抨牭漠惒酱a”。

  • 高優(yōu)先級任務(wù)(默認):所有寫在主流程中的同步代碼。它們會霸占調(diào)用棧,必須執(zhí)行完畢后,其他任何事情(包括頁面渲染、用戶點擊響應(yīng))才能發(fā)生。
  • 低優(yōu)先級任務(wù)(通過setTimeout實現(xiàn)):被 setTimeout(fn, 0) 包裹的任務(wù)。它被移出了當(dāng)前同步執(zhí)行的“快車道”,進入了異步的“慢車道”(任務(wù)隊列),需要等待所有同步代碼執(zhí)行完畢,并且調(diào)用棧清空后,才有機會被執(zhí)行。

這帶來的最大好處是:解放主線程。

在 setTimeout 的回調(diào)函數(shù)被執(zhí)行之前,瀏覽器有了一個寶貴的喘息之機。在這個間隙,它可以做更重要的事情:

  • UI 渲染:更新界面,比如顯示一個“加載中”的動畫。
  • 用戶交互:響應(yīng)用戶的點擊、滾動等事件。

實際應(yīng)用場景

場景一:防止重度計算阻塞UI

想象一下,我們需要處理一個巨大的數(shù)據(jù)集,這個操作可能需要幾百毫秒。

糟糕的實現(xiàn)(會卡死頁面):

在這個例子中,當(dāng)我們調(diào)用 handleHeavyTask 時,“正在計算…” 這條狀態(tài)更新根本不會顯示出來! 因為整個 JavaScript 主線程被 for 循環(huán)完全阻塞了,瀏覽器根本沒有機會去重新渲染頁面。用戶會看到一個凍結(jié)的界面,直到循環(huán)結(jié)束后,直接跳到“計算完成!”。

使用 setTimeout(fn, 0) 優(yōu)化:

function handleHeavyTask() {
 // 1. 先更新UI,顯示“正在計算...”
 document.getElementById('status').textContent = '正在計算...';

 // 2. 將耗時任務(wù)“降級”,放入任務(wù)隊列
 setTimeout(() => {
    // 執(zhí)行一個非常耗時的循環(huán)
    for (let i = 0; i < 2000000000; i++) {
      // some heavy calculation
    }
    // 計算完畢,更新UI
    document.getElementById('status').textContent = '計算完成!';
  }, 0);
}

現(xiàn)在,流程變成了:

  • handleHeavyTask 被調(diào)用,UI 更新為“正在計算…”。
  • setTimeout 將耗時的計算任務(wù)推入任務(wù)隊列。
  • handleHeavyTask 同步代碼執(zhí)行完畢,調(diào)用棧清空。
  • 瀏覽器獲得控制權(quán),立即執(zhí)行UI渲染,頁面上成功顯示“正在計算…”。
  • 渲染完畢后,事件循環(huán)從任務(wù)隊列中取出計算任務(wù),開始執(zhí)行。
  • 計算完成后,再次更新UI為“計算完成!”。

通過這個簡單的改動,我們極大地改善了用戶體驗。

場景二:分片處理超長任務(wù)

如果一個任務(wù)實在太龐大,即使把它放到 setTimeout 里,它依然會長時間阻塞主線程。更好的方法是將其分解成多個小塊,每處理一小塊就用 setTimeout “讓出”一次主線程。

function processHugeArray(array) {
 let i = 0;
 function chunk() {
    const end = Math.min(i + 1000, array.length);
    for (; i < end; i++) {
      // 處理1000個元素
    }

    if (i < array.length) {
      // 如果還沒處理完,將下一個分片任務(wù)放入隊列
      setTimeout(chunk, 0);
    } else {
      console.log('全部處理完畢!');
    }
  }
 // 啟動第一個分片任務(wù)
 setTimeout(chunk, 0);
}

現(xiàn)代替代方案

雖然 setTimeout(fn, 0) 非常經(jīng)典,但在現(xiàn)代前端開發(fā)中,我們有了更多針對特定場景的工具:

  • requestAnimationFrame: 如果我們的任務(wù)與視覺更新(如動畫)相關(guān),這是最佳選擇。它能保證我們的代碼在瀏覽器下一次重繪之前執(zhí)行,從而實現(xiàn)更流暢的動畫效果。
  • queueMicrotask: 用于調(diào)度微任務(wù)(Microtask)。微任務(wù)的優(yōu)先級高于宏任務(wù)(Macrotask,如 setTimeout 的回調(diào))。它會在當(dāng)前同步代碼執(zhí)行完畢后、下一次事件循環(huán)開始前立即執(zhí)行。Promise.then() 也是一個典型的微任務(wù)。
  • Web Workers: 對于真正CPU密集型的、與UI無關(guān)的計算,最好的方式是將其完全放到一個獨立的后臺線程中,這就是 Web Workers 的用武之地。它完全不會阻塞主線程。

setTimeout(fn, 0) 是一個看似簡單卻蘊含深刻原理的技巧。它不是用來精確計時的,而是一種利用事件循環(huán)機制,將任務(wù)從同步執(zhí)行流中剝離,推入異步任務(wù)隊列的手段。

通過這種“降級”操作,我們可以有效地防止長時間運行的腳本阻塞主線程,保證UI的流暢和用戶的即時響應(yīng)。下次當(dāng)我們遇到頁面卡頓時,不妨想一想,是否可以用 setTimeout(fn, 0) 為我們的任務(wù)巧妙地“讓個路”。

責(zé)任編輯:趙寧寧 來源: JavaScript
相關(guān)推薦

2021-02-02 14:55:48

React前端高優(yōu)先

2023-01-05 08:48:57

技術(shù)管理排優(yōu)先級

2015-05-20 16:51:35

谷歌云計算方案低優(yōu)先級

2021-04-09 16:39:41

鴻蒙HarmonyOS應(yīng)用

2012-08-14 09:38:29

WAN優(yōu)化

2021-07-29 06:56:35

前端事件循環(huán)

2020-09-30 09:07:37

DevOps

2023-10-25 10:21:24

瀏覽器HTTP請求

2022-06-13 10:24:47

宏任務(wù)微任務(wù)前端

2018-08-01 10:10:12

WindowsWindows10DPC

2022-12-23 09:41:14

優(yōu)先級反轉(zhuǎn)

2015-06-02 11:26:29

產(chǎn)品團隊

2009-08-28 17:10:59

C#線程優(yōu)先級

2010-09-01 14:10:36

CSS優(yōu)先級

2010-08-31 11:04:48

CSS優(yōu)先級

2010-09-13 17:30:07

CSS優(yōu)先級

2024-05-20 10:03:15

線程池優(yōu)先級隊列排序方法

2023-12-19 15:53:53

2021-04-06 10:45:18

React前端優(yōu)先級

2010-03-18 14:09:20

Java線程同步
點贊
收藏

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

主站蜘蛛池模板: 午夜寂寞福利视频 | 在线视频一区二区 | 欧美一区二区三区 | 亚州国产 | 欧美一级毛片免费观看 | 亚洲福利在线观看 | 国产精品高 | 欧美视频一区二区三区 | 真人一级毛片 | 在线播放日韩 | 久久久成人免费视频 | 成人性生交大片免费看r链接 | cao视频| 中文字幕一区在线观看视频 | 在线永久看片免费的视频 | 国产sm主人调教女m视频 | 久久成人一区 | 国产精品污www一区二区三区 | 亚洲欧美日韩精品久久亚洲区 | 男人天堂久久 | 欧美激情精品久久久久久变态 | 人人做人人澡人人爽欧美 | 国产成人精品一区二区三区 | 日韩精品在线看 | 亚洲精品一区二区三区丝袜 | 偷拍自拍网 | 中文字幕在线视频观看 | 亚洲精品播放 | 一区二区三区免费观看 | 99精品欧美一区二区蜜桃免费 | 国产a级黄色录像 | 亚洲国产一区二区三区 | 美女131mm久久爽爽免费 | 精品av天堂毛片久久久借种 | 蜜月aⅴ国产精品 | 亚洲成人精品一区 | 黄色毛片免费看 | 91久久精品一区二区三区 | 国产精品婷婷 | 国产高清在线视频 | 亚洲精品专区 |