JavaScript事件循环机制

一、JavaScript执行机制基础

1. 单线程与异步

JavaScript设计之初就是为了在浏览器中处理DOM操作,为了避免复杂的线程同步问题,采用了单线程模型。这意味着JS代码是按顺序执行的,同一时间只能做一件事。

javascript 复制代码
console.log('1');
console.log('2');
// 输出顺序永远是1, 2

但单线程并不意味着阻塞,JavaScript通过事件循环机制实现了异步非阻塞的执行模式。

2. 调用栈与任务队列

  • 调用栈(Call Stack):存储同步任务的执行上下文,后进先出(LIFO)
  • 任务队列(Task Queue):存储异步任务的回调函数,先进先出(FIFO)

当调用栈为空时,事件循环会从任务队列中取出任务压入调用栈执行。

二、同步代码与异步代码

1. 同步代码执行

同步代码会立即进入调用栈执行:

javascript 复制代码
function func1() {
  console.log('func1');
}

function func2() {
  func1();
  console.log('func2');
}

func2();
// 输出顺序: func1, func2

2. 异步代码类型

常见的异步操作包括:

  • 定时器:setTimeout, setInterval
  • 网络请求:Ajax, Fetch
  • DOM事件:click, load等
  • Promise
  • async/await
javascript 复制代码
console.log('Start');

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

Promise.resolve().then(() => {
  console.log('Promise');
});

console.log('End');
// 输出顺序: Start, End, Promise, Timeout

三、宏任务与微任务

1. 宏任务(MacroTask)

包括:

  • js脚本执行事件
  • setTimeout setInterval
  • AJAX请求完成事件
  • 用户交互事件

2. 微任务(MicroTask)

包括:

  • Promise.then/catch/finally
  • process.nextTick(Node.js环境)
  • MutationObserver

3. 执行顺序规则

  1. 执行一个宏任务(通常是script整体代码)
  2. 执行过程中遇到微任务,加入微任务队列;遇到宏任务,加入宏任务队列
  3. 当前宏任务执行完毕,立即执行所有微任务
  4. 进行UI渲染(浏览器环境)
  5. 从宏任务队列取出下一个宏任务执行
javascript 复制代码
console.log('script start'); // 宏任务1开始

setTimeout(function() {
  console.log('setTimeout'); // 宏任务2
}, 0);

Promise.resolve().then(function() {
  console.log('promise1'); // 微任务1
}).then(function() {
  console.log('promise2'); // 微任务2
});

console.log('script end'); // 宏任务1结束

/*
输出顺序:
script start
script end
promise1
promise2
setTimeout
*/

四、典型面试题解析

题目1:基础执行顺序

javascript 复制代码
console.log('1');

setTimeout(function() {
  console.log('2');
  new Promise(function(resolve) {
    console.log('3');
    resolve();
  }).then(function() {
    console.log('4');
  });
}, 0);

new Promise(function(resolve) {
  console.log('5');
  resolve();
}).then(function() {
  console.log('6');
});

setTimeout(function() {
  console.log('7');
}, 0);

console.log('8');

解析过程

  1. 同步代码执行:

    • 输出1
    • 输出5(Promise构造函数是同步的)
    • 输出8
  2. 微任务队列:

    • then回调:输出6
  3. 宏任务队列:

    • 第一个setTimeout:
      • 输出2
      • Promise构造函数输出3
      • then回调(微任务):输出4
    • 第二个setTimeout:
      • 输出7

最终输出顺序:1, 5, 8, 6, 2, 3, 4, 7

题目2:async/await执行顺序

javascript 复制代码
async function async1() {
  console.log('async1 start');
  await async2();
  console.log('async1 end');
}

async function async2() {
  console.log('async2');
}

console.log('script start');

setTimeout(function() {
  console.log('setTimeout');
}, 0);

async1();

new Promise(function(resolve) {
  console.log('promise1');
  resolve();
}).then(function() {
  console.log('promise2');
});

console.log('script end');

解析过程

  1. 同步代码:

    • 输出script start
    • 调用async1(),输出async1 start
    • 调用async2(),输出async2
    • await后面的代码相当于Promise.then,放入微任务队列
    • Promise构造函数输出promise1
    • then回调放入微任务队列
    • 输出script end
  2. 微任务队列:

    • async1 end
    • promise2
  3. 宏任务队列:

    • setTimeout输出setTimeout

最终输出顺序

script start, async1 start, async2, promise1, script end, async1 end, promise2, setTimeout

相关推荐
xnglan3 小时前
使用爬虫获取游戏的iframe地址
开发语言·爬虫·python·学习
zhysunny3 小时前
04.建造者模式的终极手册:从快餐定制到航天飞船的组装哲学
java·开发语言·建造者模式
郝学胜-神的一滴3 小时前
建造者模式:构建复杂对象的优雅方式
开发语言·c++·程序人生·建造者模式
AAIshangyanxiu4 小时前
最新基于R语言结构方程模型分析与实践技术应用
开发语言·r语言·结构方程模型·生态统计学
Fly-ping5 小时前
【前端】JavaScript 的事件循环 (Event Loop)
开发语言·前端·javascript
范纹杉想快点毕业5 小时前
基于C语言的Zynq SOC FPGA嵌入式裸机设计和开发教程
c语言·开发语言·数据库·嵌入式硬件·qt·fpga开发·嵌入式实时数据库
rockmelodies6 小时前
【PHP安全】免费解密支持:zend52、zend53、zend54好工具
开发语言·安全·php
Reggie_L6 小时前
Stream流-Java
java·开发语言·windows
巴伦是只猫6 小时前
Java 高频算法
java·开发语言·算法
程序员编程指南6 小时前
Qt容器类:QList、QMap等的高效使用
c语言·开发语言·c++·qt