diff --git a/js-advanced/eventloop/README.md b/js-advanced/eventloop/README.md new file mode 100644 index 0000000..740bc73 --- /dev/null +++ b/js-advanced/eventloop/README.md @@ -0,0 +1,28 @@ +# JavaScript 事件循环 + +## 浏览器的事件循环 + +### 执行顺序 + +- 执行一个宏任务(如 setTimeout 回调)。 +- 清空微任务队列(所有 Promise.then、queueMicrotask 等)。 +- 渲染 UI(如果需要)。 +- 重复步骤 1-3。 + +## Node.js 的事件循环 + +### 阶段划分(简化版) + +- timers:执行 setTimeout 和 setInterval 的回调。 +- I/O callbacks:处理网络、文件等 I/O 操作的回调。 +- idle, prepare:内部使用。 +- poll:用户级回调,检索新的 I/O 事件,执行 I/O 相关回调。 +- check:执行 setImmediate 的回调。 +- close callbacks:处理关闭事件(如 socket.on('close'))。 + + + +### 微任务执行时机 + +- 每个阶段结束后,会清空微任务队列(包括 Promise.then 和 queueMicrotask)。 +- process.nextTick() 会在当前阶段执行结束后立即执行,优先级高于其他微任务。 diff --git a/js-advanced/eventloop/browser/browser.js b/js-advanced/eventloop/browser/browser.js new file mode 100644 index 0000000..c00d9d2 --- /dev/null +++ b/js-advanced/eventloop/browser/browser.js @@ -0,0 +1,13 @@ +console.log('start'); + +setTimeout(() => { + console.log('setTimeout'); + Promise.resolve().then(() => console.log('Promise in setTimeout')); +}, 0); + +Promise.resolve().then(() => { + console.log('Promise 1'); + Promise.resolve().then(() => console.log('Promise 2')); +}); + +console.log('end'); \ No newline at end of file diff --git a/js-advanced/eventloop/node/1.node.js b/js-advanced/eventloop/node/1.node.js new file mode 100644 index 0000000..0cdf7f4 --- /dev/null +++ b/js-advanced/eventloop/node/1.node.js @@ -0,0 +1,27 @@ + + +console.log('start'); + +setTimeout(() => { + console.log('setTimeout'); + process.nextTick(() => console.log('nextTick in setTimeout')); +}, 0); + +setImmediate(() => { + console.log('setImmediate'); + process.nextTick(() => console.log('nextTick in setImmediate')); +}); + +process.nextTick(() => { + console.log('nextTick 1'); + process.nextTick(() => console.log('nextTick 2')); +}); + +console.log('end'); + +// 输出顺序(可能有两种情况,取决于定时器和 setImmediate 的执行时机): +// 情况 1: +// start → end → nextTick 1 → nextTick 2 → setTimeout → nextTick in setTimeout → setImmediate → nextTick in setImmediate + +// 情况 2(定时器延迟稍高时): +// start → end → nextTick 1 → nextTick 2 → setImmediate → nextTick in setImmediate → setTimeout → nextTick in setTimeout \ No newline at end of file diff --git a/js-advanced/eventloop/node/2.fs.js b/js-advanced/eventloop/node/2.fs.js new file mode 100644 index 0000000..d4ac66f --- /dev/null +++ b/js-advanced/eventloop/node/2.fs.js @@ -0,0 +1,26 @@ +const fs = require('fs'); + +// 1. timers 阶段执行 setTimeout +setTimeout(() => { + console.log('setTimeout'); + process.nextTick(() => console.log('nextTick in setTimeout')); // 阶段结束后执行 + Promise.resolve().then(() => console.log('Promise in setTimeout')); // 后于 nextTick +}, 0); + +// 2. poll 阶段执行 I/O 回调 +fs.readFile(__filename, () => { + console.log('fs.readFile'); + process.nextTick(() => console.log('nextTick in fs.readFile')); // 阶段结束后执行 + Promise.resolve().then(() => console.log('Promise in fs.readFile')); // 后于 nextTick +}); + +// 3. check 阶段执行 setImmediate +setImmediate(() => { + console.log('setImmediate'); + process.nextTick(() => console.log('nextTick in setImmediate')); // 阶段结束后执行 + Promise.resolve().then(() => console.log('Promise in setImmediate')); // 后于 nextTick +}); + +// 主线程代码(属于 poll 阶段前的初始化) +process.nextTick(() => console.log('nextTick 1')); // 主线程结束后立即执行 +Promise.resolve().then(() => console.log('Promise 1')); // 后于 nextTick \ No newline at end of file diff --git a/js-advanced/eventloop/node/3.io-pool.js b/js-advanced/eventloop/node/3.io-pool.js new file mode 100644 index 0000000..6f476ae --- /dev/null +++ b/js-advanced/eventloop/node/3.io-pool.js @@ -0,0 +1,22 @@ +const net = require('net'); +const fs = require('fs'); + +// 读取文件(异步操作) +fs.readFile(__filename, (err, data) => { + console.log('File read complete'); // 此回调在 poll 阶段执行 +}); + +// 定时器和 setImmediate 的执行顺序受 poll 阶段影响 +setTimeout(() => console.log('setTimeout'), 0); +setImmediate(() => console.log('setImmediate')); + +// 连接一个不存在的服务器(触发 ECONNREFUSED 错误) +const socket = net.connect(18080, 'localhost', () => { + console.log('Connected'); +}); + +socket.on('error', (err) => { + console.log('Error:', err.code); // 此回调在 I/O callbacks 阶段执行 +}); + +// setTimeout -> error -> File read complete -> setImmediate \ No newline at end of file diff --git a/js-advanced/eventloop/node/4.loop.js b/js-advanced/eventloop/node/4.loop.js new file mode 100644 index 0000000..163d734 --- /dev/null +++ b/js-advanced/eventloop/node/4.loop.js @@ -0,0 +1,57 @@ +const fs = require('fs'); +const net = require('net'); + +console.log('=== 开始第一次事件循环 ==='); + +// 首次事件循环 - timers阶段 +setTimeout(() => { + console.log('\n=== 第二次事件循环 - timers阶段 ==='); + console.log('执行 setTimeout 回调'); + + // 微任务会在阶段结束后执行 + process.nextTick(() => console.log('timers 阶段的 nextTick')); + Promise.resolve().then(() => console.log('timers 阶段的 Promise')); + + // 安排一个在check阶段执行的任务 + setImmediate(() => { + console.log('\n=== 第三次事件循环 - check阶段 ==='); + console.log('执行 setImmediate 回调'); + }); + + // 安排一个在下次timers阶段执行的任务 + setTimeout(() => { + console.log('\n=== 第四次事件循环 - timers阶段 ==='); + console.log('执行嵌套的 setTimeout 回调'); + }, 0); +}, 0); + +// 首次事件循环 - poll阶段(I/O操作) +fs.readFile(__filename, (err, data) => { + console.log('\n=== 第二次事件循环 - poll阶段(I/O回调) ==='); + console.log('执行 fs.readFile 回调'); + + // 微任务会在阶段结束后执行 + process.nextTick(() => console.log('poll 阶段的 nextTick')); + Promise.resolve().then(() => console.log('poll 阶段的 Promise')); +}); + +// 首次事件循环 - 模拟网络I/O +const server = net.createServer(); +server.listen(18080, () => { + console.log('\n=== 第二次事件循环 - poll阶段(网络监听) ==='); + console.log('服务器已启动,监听端口 8080'); + + // 关闭服务器,触发close callbacks阶段 + server.close(() => { + console.log('\n=== 第二次事件循环 - close callbacks阶段 ==='); + console.log('服务器已关闭'); + }); +}); + +// 首次事件循环 - 主线程结束后的微任务 +process.nextTick(() => console.log('\n主线程的 nextTick')); +Promise.resolve().then(() => console.log('主线程的 Promise')); + +console.log('=== 首次事件循环 - 主线程执行完毕 ==='); + +// \ No newline at end of file