JavaScript事件循环

js 复制代码
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');
});

async1();
console.log('script start');
new Promise(function(resolve) {
  console.log('promise1');
  resolve();
}).then(function() {
  console.log('promise2');
});
输出:
script start
async1 start
async2
script start
promise1
async1 end
promise2
setTimeout

js是一个单线程,会阻塞代码

为了防止阻塞代码,把代码分为同步和异步

同步代码交给js引擎执行(js只能是单线程)

异步代码交给宿主环境(浏览器/node,可以支持多线程)

同步代码放到执行栈,异步代码等时机成熟会放到任务队列里

执行栈执行完,会去任务队列看是否有异步任务,有就推送到执行栈执行,反复循环查看执行,这个过程就是事件循环(eventloop)

1.同步任务和异步任务

1.定时器

js 复制代码
console.log(1)
setTimeout(() => {
  console.log(2)
}, 0)
console.log(3)
输出:132

js是单线程的,同一时间只能做一件事

作为浏览器脚本语言,与它的用途有关

js主要用途是和用户互动,以及操作DOM,这决定了它只能是单线程

例如如果线程1和线程2分别是添加和删除同一个节点,执行顺序不同会导致结果的不同

所以只能先添加再删除

总不能等计时器结束才执行吧,总不能等到事件监听点击之后才执行吧

异步代码的共同点:它们都是耗时的

同步代码放到执行栈,异步代码放到宿主环境

执行栈:里的代码立即执行,并且原地等待结果

宿主环境的时间一到,就推送到任务队列

执行栈执行完了,就会看任务队列有没有异步任务需要执行

此时再把任务队列的异步函数的回调函数推送到执行栈

执行栈只要同步代码一执行完,就会反复到任务队列去看有没有异步的任务需要执行

从执行栈到任务队列反复查找的过程就叫事件循环

2.异步任务(宏任务/微任务)

js把异步任务分为宏任务和微任务

ES5之后,js引入了Promise

这样,即使不需要浏览器,js引擎自身也能发起异步任务

宏任务由宿主(浏览器、Node)发起

微任务由JS引擎发起

主要的异步任务有Promise

不过Promise本身同步,里面的then/catch的回调函数是异步的

代码示例
js 复制代码
console.log(1);
setTimeout(function () {
    console.log(2);
}, 0);

const p = new Promise((resolve, reject) => {
    console.log(3);
    resolve(1000); // 标记为成功
    console.log(4);
});

p.then(data => {
    console.log(data);
});

console.log(5);
输出:1 3 4 5 1000 2
js 复制代码
new Promise((resolve, reject) => {
  resolve(1);


  new Promise((resolve, reject) => {
    resolve(2);
  }).then(data => {
    console.log(data);
    });
}).then(data => {
  console.log(data);
});

console.log(3);
输出:3 2 1
js 复制代码
console.log(11);

setTimeout(() => {
  console.log(12);
  let p = new Promise((resolve) => {
    resolve(13);
  });
  p.then(res => {
    console.log(res);
  });
}, 0);

console.log(15);

console.log(14);
输出:11 15 14 12 13
js 复制代码
setTimeout(() => {
  console.log(1);
}, 0);

new Promise((resolve, reject) => {
  console.log(2);
  resolve('p1');
})
new Promise((resolve, reject) => {
  console.log(3);
  setTimeout(() => {
    resolve('setTimeout2');
    console.log(4);
  }, 0);
  resolve('p2');
}).then(data => {
  console.log(data);
});

setTimeout(() => {
  resolve('setTimeout1');
  console.log(5);
}, 0).then(data => {
  console.log(data);
});

console.log(6);
js 复制代码
setTimeout(() => {
  console.log(1);
}, 0);

new Promise((resolve, reject) => {
  console.log(2);
  resolve('p1');

  new Promise((resolve, reject) => {
    console.log(3);
    setTimeout(() => {
      resolve('setTimeout2');
      console.log(4);
    }, 0);
    resolve('p2');
  }).then(data => {
      console.log(data);
})

  setTimeout(() => {
      resolve('setTimeout1');
      console.log(5);
  }, 0)
}).then(data => {
    console.log(data);
});

console.log(6);

输出:2 3 6 p2 p1 1 4 5
定时器里的resolve('setTimeout2')、resolve('setTimeout1')是迷惑性的
promise里有resolve,定时器里是不执行的

async函数里的代码属于同步任务,await后面所有的代码,在函数后面的所有代码都属于微任务,等代码执行完了才去执行

js 复制代码
async function async1() {
  console.log('async1 start');
  await async2(); //await下面的代码暂时是不执行的,除非右边全部执行完了才会去执行
  console.log('async1 end');
}

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

console.log('script start');

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

async1();
输出:script start/async1 start/async2/async1 end/setTimeout
相关推荐
Lee川2 分钟前
Prisma 实战指南:像搭积木一样设计古诗词数据库
前端·数据库·后端
Linsk2 分钟前
Java和JavaScript的关系真是雷峰和雷峰塔的关系吗?
java·javascript·oracle
当时只道寻常6 分钟前
浏览器文本复制到剪贴板:企业级最佳实践
javascript
许彰午16 分钟前
我手写了一个 Java 内存数据库(二):B+ 树的插入与分裂
java·开发语言·面试
jinanwuhuaguo18 分钟前
(第二十九篇)OpenClaw 实时与具身的跃迁——从异步孤岛到数字世界的“原住民”
前端·网络·人工智能·重构·openclaw
广州华水科技24 分钟前
深度测评2026年单北斗GNSS位移监测系统推荐,与高口碑变形监测设备一同引领行业新风尚
前端
大飞记Python31 分钟前
【2026更新】Python基础学习指南(AI版)——04数据类型
开发语言·人工智能·python
Alice-YUE1 小时前
【js高频八股】防抖与节流
开发语言·前端·javascript·笔记·学习·ecmascript
云泽8081 小时前
C++11 核心特性全解:列表初始化、右值引用与移动语义实战
开发语言·c++