Chuyển đến nội dung chính

レッスン 2: イベント ループと非同期パターン

イベント ループのフェーズ (タイマー、保留中、ポーリング、チェック、クローズ)。マイクロタスクとマクロタスク。 process.nextTick と queueMicrotask と setImmediate の比較。コールバック、プロミス、非同期/待機。 Promise.allSettled、Promise.any。

💻 プログラミング — レッスン 2 レッスン 2: イベント ループと非同期パターン

Node.js コア: 基本から高度まで

パート 1: Node.js の内部構造

xdev.asia

1. イベントループフェーズ

   ┌───────────────────────────┐
┌─>│         timers            │  setTimeout, setInterval
│  └──────────┬────────────────┘
│  ┌──────────┴────────────────┐
│  │     pending callbacks     │  I/O callbacks deferred
│  └──────────┬────────────────┘
│  ┌──────────┴────────────────┐
│  │       idle, prepare       │  internal use
│  └──────────┬────────────────┘
│  ┌──────────┴────────────────┐
│  │         poll              │  I/O events (fs, net)
│  └──────────┬────────────────┘
│  ┌──────────┴────────────────┐
│  │         check             │  setImmediate
│  └──────────┬────────────────┘
│  ┌──────────┴────────────────┐
└──│      close callbacks      │  socket.on('close')
   └───────────────────────────┘

2. マイクロタスクとマクロタスク

console.log('1: Script start')

setTimeout(() => console.log('2: setTimeout'), 0)       // Macrotask (timers)
setImmediate(() => console.log('3: setImmediate'))       // Macrotask (check)

Promise.resolve().then(() => console.log('4: Promise'))  // Microtask
queueMicrotask(() => console.log('5: queueMicrotask'))   // Microtask
process.nextTick(() => console.log('6: nextTick'))       // Microtask (ưu tiên cao nhất)

console.log('7: Script end')

// Output:
// 1: Script start
// 7: Script end
// 6: nextTick        ← nextTick luôn chạy trước
// 4: Promise
// 5: queueMicrotask
// 2: setTimeout      ← hoặc 3 trước (phụ thuộc event loop)
// 3: setImmediate

3. process.nextTick と queueMicrotask の比較

APIキュー注文いつ使用するか
process.nextTick次のティックキュー最高現在の操作の直後に必ず実行してください
キューマイクロタスクマイクロタスクキュー次のティックの後標準準拠、Web 互換
約束してください。それではマイクロタスクキュー次のティックの後非同期チェーン
setTimeout(fn, 0)タイマーフェーズマクロタスク次のイベントループの繰り返しまで延期する
セット即時チェックフェーズマクロタスクI/Oポーリング後に実行

4. 非同期パターンの進化

import { readFile } from 'node:fs'
import { readFile as readFileAsync } from 'node:fs/promises'

// 1. Callback (old style)
readFile('/etc/hosts', 'utf8', (err, data) => {
  if (err) throw err
  console.log(data)
})

// 2. Promise
readFileAsync('/etc/hosts', 'utf8')
  .then(data => console.log(data))
  .catch(err => console.error(err))

// 3. Async/Await (recommended)
async function main() {
  try {
    const data = await readFileAsync('/etc/hosts', 'utf8')
    console.log(data)
  } catch (err) {
    console.error(err)
  }
}

5. Promise コンビネータ

const tasks = [
  fetch('https://api.example.com/users'),
  fetch('https://api.example.com/posts'),
  fetch('https://api.example.com/comments'),
]

// Promise.all — fail nếu 1 reject
const [users, posts, comments] = await Promise.all(tasks)

// Promise.allSettled — không fail, trả status
const results = await Promise.allSettled(tasks)
results.forEach(r => {
  if (r.status === 'fulfilled') console.log(r.value)
  else console.error(r.reason)
})

// Promise.any — trả kết quả đầu tiên thành công
const fastest = await Promise.any(tasks)

// Promise.race — trả kết quả đầu tiên (fulfilled hoặc rejected)
const first = await Promise.race(tasks)

6. アボートコントローラー

const controller = new AbortController()
const { signal } = controller

// Timeout tự động cancel
setTimeout(() => controller.abort(), 5000)

try {
  const response = await fetch('https://api.example.com/data', { signal })
  const data = await response.json()
} catch (err) {
  if (err instanceof DOMException && err.name === 'AbortError') {
    console.log('Request was aborted')
  }
}

次の記事: モジュールシステムとパッケージ管理 — CJS 対 ESM、npm、pnpm。