一、为什么需要 Event Loop?

JavaScript 是 单线程语言,意味着它一次只能执行一个任务。但浏览器中需要处理大量异步操作(如网络请求、定时器、用户事件),如果让主线程等待这些操作完成,会导致页面“卡死”。Event Loop(事件循环) 的机制让 JS 在单线程下也能实现非阻塞异步执行。通过 Event Loop 机制实现了:

  • 1.非阻塞 I/O 操作
  • 2.异步任务处理
  • 3.高并发处理能力

二、核心架构图解

┌───────────────────────┐
│        Call Stack     │ ← 同步代码执行
└──────────┬────────────┘
           │
           │
┌──────────▼────────────┐
│   Web APIs/DOM APIs   │ ← 定时器/事件监听/网络请求
└──────────┬────────────┘
           │
           │
┌──────────▼────────────┐
│  Task Queue (Macro)   │ ← setTimeout/setInterval/UI事件
├───────────────────────┤
│  Microtask Queue      │ ← Promise/MutationObserver
└──────────┬────────────┘
           │
┌──────────▼────────────┐
│     Event Loop        │ ← 持续检查队列的调度机制
└───────────────────────┘

三、执行顺序规则

  1. 1.执行同步代码直至调用栈清空
  2. 2.执行所有微任务(Microtasks)
  3. 3.执行一个宏任务(Macrotask)
  4. 4.重复循环

四、代码演示

console.log('1. Script Start');


setTimeout(() => {
  console.log('6. setTimeout');
}, 0);


new Promise((resolve) => {
  console.log('2. Promise Constructor');
  resolve();
})
.then(() => {
  console.log('4. Promise Then 1');
})
.then(() => {
  console.log('5. Promise Then 2');
});


console.log('3. Script End');


// 输出顺序:
// 1. Script Start
// 2. Promise Constructor
// 3. Script End
// 4. Promise Then 1
// 5. Promise Then 2
// 6. setTimeout

五、任务类型分类

任务类型

示例

优先级

微任务(Micro)

Promise.then / queueMicrotask


宏任务(Macro)

setTimeout / setInterval


渲染任务

requestAnimationFrame

特殊

 

六、实战应用

1. 性能优化

// 错误示例:阻塞主线程
function longTask() {
  for(let i=0; i<1e7; i++){ 
    // 长时间同步计算
  }
}


// 正确做法:分解任务
function chunkedTask() {
  let i = 0;
  function processChunk() {
    while(i < 1e7 && performance.now() - start < 50) {
      i++
    }
    if(i < 1e7) {
      setTimeout(processChunk);
    }
  }
  const start = performance.now();
  processChunk();
}

2. 优先级控制

// 微任务优先处理
button.addEventListener('click', () => {
  Promise.resolve().then(() => {
    console.log('Microtask handling click');
  });
  setTimeout(() => {
    console.log('Macrotask handling click');
  });
});

3. 混合任务处理

console.log('Start');


setTimeout(() => console.log('Timeout'), 0);


Promise.resolve()
  .then(() => {
    console.log('Promise 1');
    queueMicrotask(() => console.log('Microtask in Promise'));
  })
  .then(() => console.log('Promise 2'));


console.log('End');


/* 输出顺序:
Start
End
Promise 1
Promise 2
Microtask in Promise
Timeout
*/

七、常见问题

1. 函数节流优化

function throttle(fn, delay) {
  let lastCall = 0;
  return (...args) => {
    const now = Date.now();
    if(now - lastCall >= delay) {
      fn(...args);
      lastCall = now;
    } else {
      requestAnimationFrame(() => throttle(fn, delay)(...args));
    }
  };
}

2. 竞态条件处理

let controller = new AbortController();


async function fetchData() {
  try {
    const response = await fetch(url, {
      signal: controller.signal
    });
    // 处理响应
  } catch (e) {
    if (e.name === 'AbortError') {
      console.log('请求已取消');
    }
  }
}


// 取消前一个请求
function refreshData() {
  controller.abort();
  controller = new AbortController();
  fetchData();
}

Web Workers 集成

// 主线程
const worker = new Worker('worker.js');


worker.onmessage = (e) => {
  console.log('Received:', e.data);
};


worker.postMessage('Start');


// worker.js
self.onmessage = (e) => {
  const result = heavyCalculation(e.data);
  self.postMessage(result);
};

理解 Event Loop 的运行机制所带来的优势:

  • 1.编写更高效的异步代码
  • 2.避免常见性能陷阱
  • 3.实现流畅的交互体验
  • 4.更好地处理复杂任务调度