中文字幕精品亚洲无线码二区,国产黄a三级三级三级看三级,亚洲七七久久桃花影院,丰满少妇被猛烈进入,国产小视频在线观看网站

【每(mei)日一面】setTimeout 延時為 0 的(de)情況

基礎問答

問題:你在寫代碼的過程中,在什么時候才會設置 setTimeout 的延時為 0?

回答:有如下幾種情況

  1. 避免同步任務阻塞 UI,即在渲染較多數據的時候,可以通過 setTimeout 分批渲染。
const data = new Array(1000).fill(1).map((x, idx) => idx + 1);

function render(list) {
  let index = 0;
  for (; index < list.length; index += 100) {
    console.log('current', index);
    const current = index;
    setTimeout(() => { 
      console.log(list.slice(current, current + 100).join(','))
    }, 0);
  }
}

render(data);
  1. 獲取 DOM 元素的寬高,本質是根據事件循環機制調整了代碼的執行順序。
function App() {
  const dom = document.querySelector('#app');
  console.log(dom.height);
  setTimeout(() => dom.height, 0);
}
  1. 代碼分片,古早技術,將同步代碼分片執行,避免阻塞渲染。

擴展延伸

JavaScript 單線程:JavaScript 是單線程語言,這個是編程語言的設計,在同一時間只能執行一段代碼,所有的任務都需要排隊,而身為單線程,但是好像我們訪問網頁的時候還是那么快,這語言優勢這么強?這是另一個問題,語言設計上是單線程,只能同步的執行代碼,但是瀏覽器不是,他是多線程的,分出來一個 JS 主線程用于執行 JavaScript 代碼,還有如 UI 線程,用于執行渲染等。在 JavaScript 中,通過事件循環來(lai)協調任務(wu)執行,實現異步編程。

事件循環:這個(ge)(ge)機(ji)制是 JavaScript 的一(yi)個(ge)(ge)核心機(ji)制,可以利用(yong)這個(ge)(ge)機(ji)制實現高并發,異步(bu)編程操作。

核心是 - 調用棧、任務隊列、宏任務、微任務

整個流程為(wei) - JavaScript 代碼按(an)照代碼依(yi)次(ci)執(zhi)行時,檢(jian)測(ce)(ce)到同步任(ren)(ren)務就進(jin)入調(diao)用棧執(zhi)行,檢(jian)測(ce)(ce)到宏任(ren)(ren)務,先壓入宏任(ren)(ren)務隊列(lie),檢(jian)測(ce)(ce)到微(wei)(wei)任(ren)(ren)務,則(ze)壓入微(wei)(wei)任(ren)(ren)務隊列(lie),當(dang)本輪同步任(ren)(ren)務(宏任(ren)(ren)務)結束時,檢(jian)測(ce)(ce)微(wei)(wei)任(ren)(ren)務隊列(lie),清(qing)空(即執(zhi)行所(suo)有的微(wei)(wei)任(ren)(ren)務),這個檢(jian)測(ce)(ce)的時機稱為(wei)“微(wei)(wei)任(ren)(ren)務檢(jian)查點”。

yuque_diagram (1)

如(ru)圖,伴(ban)隨(sui)著每個(ge)宏任(ren)務(wu)執行,都有自己對(dui)應的微任(ren)務(wu)隊列(lie),直到微任(ren)務(wu)隊列(lie)全部(bu)執行完成(cheng),才會(hui)開啟下一個(ge)宏任(ren)務(wu)。

setTimeout(callback, delayTime) API:在(zai)執(zhi)(zhi)(zhi)行(xing)(xing)這個 API 時,JS 引擎會將 callback 函數封(feng)裝成宏任(ren)(ren)務,掛載到(dao)(dao)延(yan)遲(chi)(chi)隊(dui)列(lie)中,等待執(zhi)(zhi)(zhi)行(xing)(xing)。這里再次引入了一個新的(de)(de)概念,延(yan)遲(chi)(chi)隊(dui)列(lie),這個是瀏覽(lan)器(qi)(或者引擎)實現的(de)(de),當 JavaScript 創建(jian)定時器(qi)的(de)(de)時候(hou),渲染進程就會將這個定時器(qi)的(de)(de)任(ren)(ren)務添(tian)加到(dao)(dao)延(yan)遲(chi)(chi)隊(dui)列(lie)中。執(zhi)(zhi)(zhi)行(xing)(xing)完一個任(ren)(ren)務,計算延(yan)遲(chi)(chi)隊(dui)列(lie)中是否有(you)到(dao)(dao)期(qi)的(de)(de)任(ren)(ren)務,有(you)就執(zhi)(zhi)(zhi)行(xing)(xing),沒有(you)繼(ji)續循(xun)環。

面試追問

  1. 延遲時間為 0,會立即執行嗎?

不(bu)會(hui),雖然我們(men)設(she)置為了 0,但是(shi) setTimeout 的(de)(de)回(hui)調(diao)函數會(hui)被封裝(zhuang)成一個宏任(ren)(ren)務(wu),所(suo)以(yi)(yi)他需要等待同步任(ren)(ren)務(wu)執行結(jie)束后(hou),從宏任(ren)(ren)務(wu)隊列中取出來執行。此外(wai),這個延遲時(shi)間雖然可以(yi)(yi)設(she)置為 0,但是(shi)瀏(liu)覽(lan)器的(de)(de)最小(xiao)執行時(shi)間實際是(shi)不(bu)一定的(de)(de),Chrome 瀏(liu)覽(lan)器是(shi) 4ms。

  1. 那延遲時間設置為 400ms,會在 400ms 時執行嗎?

不會,原(yuan)因(yin)同上。setTimeout 只(zhi)能做(zuo)到“盡(jin)快執行”,而不是(shi)“立即執行”。

  1. 你在使用 setTimeout 的時候,有遇到過什么問題嗎?

歷史代碼問題,存在比較多的 setTimeout 導致代碼執行(xing)的(de)結(jie)果(guo)不(bu)好理解。

this 指針問題,setTimeout 回(hui)調(diao)函數中的 this 和直覺不(bu)(bu)符,如果執(zhi)行的回(hui)調(diao)函數是(shi)一個(ge)(ge)對象的方法(fa),那么這個(ge)(ge)對象的方法(fa)中 this 并(bing)不(bu)(bu)是(shi)指向這個(ge)(ge)對象,而是(shi)全局。

長(chang)任務阻(zu)塞延(yan)遲(chi)的回調(diao)函數(shu)調(diao)用,如(ru)果當前(qian)任務執行的時(shi)間比較長(chang),可能會(hui)導致回調(diao)函數(shu)等待。

瀏(liu)(liu)覽器優化問題(ti),現(xian)在瀏(liu)(liu)覽器為了降低對電(dian)量的(de)消耗,延(yan)長續航時間(jian),會對后臺(tai)界面的(de) setTimeout 執行時間(jian)間(jian)隔延(yan)長,一般會大于 1s,但是遇(yu)到(dao)過更久的(de),有一個多小(xiao)時。

  1. 那有沒有可以替代的 API?

有,和動畫相關的可以使用 requestAnimationFrame API 來(lai)替代,可以保持和瀏覽器渲(xuan)染頻率一致,而不需要計算每幀的間隔時間來(lai)延遲執行。

微(wei)任務(wu)可以使用 Promise 來創(chuang)建。

  1. 實現一個簡單的 setTimeout。
/**
 * 用 requestAnimationFrame 實現簡易 setTimeout
 * @param {number} delay - 延遲時間(毫秒)
 * @returns {number} - RAF的ID,用于取消(對應clearTimeout)
 */?
function rafSetTimeout(callback, delay) {
  // 1. 記錄延遲結束的目標時間(當前時間 + 延遲時間)
  const startTime = Date.now();
  const targetTime = startTime + delay;

  // 2. 定義遞歸執行的RAF回調函數
  function rafCallback() {
    // 3. 檢查當前時間是否達到目標時間
    if (Date.now() >= targetTime) {
      // 達到目標時間,執行用戶回調
      callback();
    } else {
      // 未達到,繼續遞歸調用RAF,等待下一次重繪
      requestAnimationFrame(rafCallback);
    }
  }

  // 4. 啟動第一次RAF,開始等待
  return requestAnimationFrame(rafCallback);
}

/**
 * 對應 clearTimeout,取消 rafSetTimeout
 * @param {number} rafId - rafSetTimeout 返回的RAF ID
 */
function rafClearTimeout(rafId) {
  cancelAnimationFrame(rafId);
}
  1. 經典題目,判斷運行結果,這里給個簡單的例子。
setTimeout(() => {
  console.log('回調1');
}, 0);

// 插入同步任務
console.log('同步任務');

setTimeout(() => {
  console.log('回調2');
}, 0);
posted @ 2025-09-29 10:16  Achieve前端實驗室  閱讀(264)  評論(0)    收藏  舉報