1. イベントエミッタパターン
import { EventEmitter } from 'node:events'
// Typed EventEmitter
interface OrderEvents {
created: [order: { id: string; total: number }]
paid: [orderId: string, amount: number]
shipped: [orderId: string]
error: [error: Error]
}
class OrderService extends EventEmitter<OrderEvents> {
async createOrder(data: { items: string[]; total: number }) {
const order = { id: crypto.randomUUID(), total: data.total }
this.emit('created', order)
return order
}
}
const service = new OrderService()
service.on('created', (order) => {
console.log(`Order ${order.id} created: $${order.total}`)
})
// Once — lắng nghe 1 lần
service.once('paid', (orderId, amount) => {
console.log(`${orderId} paid $${amount}`)
})
// Async iterator
const ac = new AbortController()
for await (const [order] of EventEmitter.on(service, 'created', { signal: ac.signal })) {
console.log(`New order: ${order.id}`)
}
2. AbortController と AbortSignal
// Timeout signal
const signal = AbortSignal.timeout(5000)
// Combine signals
const controller = new AbortController()
const combined = AbortSignal.any([
controller.signal,
AbortSignal.timeout(10000),
])
// Sử dụng với EventEmitter
const emitter = new EventEmitter()
const promise = EventEmitter.once(emitter, 'data', { signal: combined })
// Cancel
controller.abort('User cancelled')
3. 診断チャネル
import diagnostics_channel from 'node:diagnostics_channel'
// Subscribe to HTTP requests
diagnostics_channel.subscribe('http.client.request.start', (message) => {
const { request } = message as any
console.log(`HTTP → ${request.method} ${request.host}${request.path}`)
})
// Custom channel
const channel = diagnostics_channel.channel('myapp:db:query')
channel.subscribe((message) => {
const { query, duration } = message as { query: string; duration: number }
if (duration > 100) {
console.warn(`Slow query (${duration}ms): ${query}`)
}
})
// Publish
channel.publish({ query: 'SELECT * FROM users', duration: 150 })
4. メモリリークの検出
import v8 from 'node:v8'
import { writeFileSync } from 'node:fs'
// Heap snapshot
function takeHeapSnapshot() {
const snapshotStream = v8.writeHeapSnapshot()
console.log(`Heap snapshot: ${snapshotStream}`)
}
// Memory monitoring
setInterval(() => {
const { heapUsed, heapTotal, rss } = process.memoryUsage()
console.log({
heapUsed: `${(heapUsed / 1e6).toFixed(1)}MB`,
heapTotal: `${(heapTotal / 1e6).toFixed(1)}MB`,
rss: `${(rss / 1e6).toFixed(1)}MB`,
})
// Alert if memory exceeds threshold
if (heapUsed > 500 * 1e6) {
takeHeapSnapshot()
console.error('Memory threshold exceeded!')
}
}, 10000)
// EventEmitter leak warning
const emitter = new EventEmitter()
emitter.setMaxListeners(20) // Default: 10
5. Node.js インスペクター API
import inspector from 'node:inspector/promises'
// Programmatic profiling
const session = new inspector.Session()
session.connect()
await session.post('Profiler.enable')
await session.post('Profiler.start')
// ... run code to profile ...
const { profile } = await session.post('Profiler.stop')
// Save profile for Chrome DevTools
次の記事: HTTP フレームワークを最初から構築する — ルーター、ミドルウェア パイプライン。