You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

关于JS事件循环与浏览器渲染阻塞的实验疑问求解

JS事件循环与渲染阻塞实验解析

实验背景

观看《What the heck is the event loop anyway?》和《Web browser event loop, setTimeout, micro tasks, requestAnimationFrame...》后,通过四个实验探究不同延迟任务对浏览器渲染的阻塞情况,以下是各实验的行为解析及疑问解答。

各实验行为解析

1. 同步长任务(delayLoop)

你的理解完全正确:同步任务会直接占用主线程调用栈,在任务执行完毕前调用栈不会空闲,浏览器无法处理渲染任务队列,因此UI会全程冻结直到任务结束。

2. Promise回调执行长任务(delayPromise)

你的认知没有问题:Promise.resolve().then()的回调属于微任务,微任务会在当前宏任务执行完毕后、下一个宏任务启动前全部执行完毕。当func这个宏任务执行完成后,会立即执行该微任务内的长循环,此时调用栈被占用,浏览器依然无法插入渲染任务,因此渲染会被延迟到微任务执行完成后。

3. 新建Promise时在resolve前执行长任务(delayPromiseReturn)

你疑惑的核心误区在于:Promise的executor函数(即new Promise(resolve => { ... })中的回调)是同步执行的。

当你在func中调用delayPromiseReturn()时,函数会立即创建Promise,同时同步执行executor内的所有代码:先打印starting await,然后执行长循环,循环结束后打印ending await,最后调用resolve()——整个过程完全同步,会持续占用调用栈直到全部完成。

也就是说,delayPromiseReturn()的执行是同步阻塞的,它会卡住func后续代码的执行:只有当executor里的长循环跑完,func才会继续执行console.log('ending delay func')。这和Promise回调的异步性无关,executor本身就是同步执行的逻辑。

4. setTimeout回调执行长任务(delayTimeout)

这个超出预期的行为,核心在于浏览器的任务调度机制与合成线程独立运行:

  • setTimeout的回调属于宏任务,会被加入宏任务队列,在指定延迟(此处5秒)后等待主线程空闲时执行。
  • 你写的动画是transform: rotate,这属于合成层动画——这类动画由浏览器的合成线程独立处理,不需要主线程参与(只有涉及DOM重排/重绘的动画才会依赖主线程)。

点击按钮后,func作为宏任务执行完毕,主线程空闲,浏览器立即处理渲染任务,启动动画。5秒后setTimeout的回调进入宏任务队列并开始执行长循环,此时主线程被阻塞,但合成线程不受影响,依然能正常完成动画渲染,因此你会看到动画正常执行,而setTimeout的回调在主线程后台跑完后才打印日志。

实验代码

JavaScript

function delayLoop() {
    console.log('starting for loop')
    for(let i = 0; i < 10000000000; i++) {}
  console.log('ending for loop')
}

function delayPromise() {
    Promise.resolve().then(function() {
    console.log('starting promise')
    for(let i = 0; i < 10000000000; i++) {}
    console.log('ending promise')
  })
}

function delayPromiseReturn() {
    return new Promise(function(resolve) {
    console.log('starting await')
    for(let i = 0; i < 10000000000; i++) {}
    console.log('ending await')
    
    resolve()
  })
}

function delayTimeout() {
    setTimeout(function() {
    console.log('starting timeout')
    for(let i = 0; i < 10000000000; i++) {}
    console.log('ending timeout')
  }, 5000)
}

function func() {
    console.log('starting delay func')
    
  document.querySelector('.anim').classList.add('start')
    /* delayLoop() */
  /* delayPromise() */
  /* delayPromiseReturn() */
  
  delayTimeout()
  
  console.log('ending delay func')
}

CSS

.anim {
  height: 50px;
  width: 50px;
  background-color: blue;
}

@keyframes rotation {
  0% {
    transform: rotate(0deg);
  }
  
  100% {
    transform: rotate(90deg);
  }
}

.start {
  animation: rotation 1s linear 10;
}

HTML

<button onclick="func()">Click</button>
<div class="anim"></div>

内容的提问来源于stack exchange,提问作者Devansh Sharma

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.09 05:32:51