01Event Loop mental model
JavaScript ของหนึ่ง process ทำงานหลักบน thread เดียว งาน I/O ถูกส่งให้ระบบปฏิบัติการหรือ worker pool แล้ว callback กลับมาเข้าคิว เมื่อ call stack ว่าง Event Loop จึงหยิบงานถัดไป ข้อดีคือไม่ต้องสร้าง thread ต่อ connection แต่ callback ที่ทำงานนานจะทำให้ request อื่นรอทั้งหมด
Promise callbacks และ `queueMicrotask` อยู่ใน microtask queue ซึ่งถูก drain ก่อน Event Loop ไป phase ถัดไป การสร้าง microtask ต่อเนื่องจำนวนมากจึงทำให้ timer และ I/O callback หิวงานได้ แม้ไม่ได้ใช้ synchronous loop
02Promise, cancellation และ error propagation
`async/await` ทำให้ control flow อ่านง่าย แต่ต้องกำหนดว่า failure แบบใดควรหยุดทั้งหมด หากทุกผลลัพธ์จำเป็น `Promise.all` ให้ fail-fast หากต้องเก็บผลสำเร็จและล้มเหลวทุกตัวใช้ `Promise.allSettled` และหากรันทีละตัวให้ใช้ `for...of`—ไม่ใช้ `forEach(async ...)` เพราะ caller ไม่ได้รอ promise ภายใน
async function getPolicy(id: string, signal: AbortSignal) {
const response = await fetch(`/internal/policies/${id}`, { signal });
if (!response.ok) throw new UpstreamError(response.status);
return policySchema.parse(await response.json());
}
// ทุกผลลัพธ์จำเป็นและยอมให้ fail-fast
const [policy, payment] = await Promise.all([
getPolicy(id, signal),
getPayment(id, signal),
]);ส่ง `AbortSignal` ลงไปถึง HTTP client หรือ operation ที่รองรับเพื่อยกเลิกงานเมื่อ request จบ และมี centralized error handler สำหรับแปลง domain error เป็น HTTP response โดย log หนึ่งครั้งที่ boundary พร้อม correlation ID
03Stream และ Backpressure
Stream ช่วยประมวลผลข้อมูลเป็น chunk โดยไม่ต้องโหลดไฟล์ทั้งก้อนเข้า memory แต่หัวใจคือ backpressure: เมื่อ consumer ช้ากว่า producer ระบบต้องชะลอการอ่านหรือเขียน มิฉะนั้น buffer จะโตจน process ใช้ memory สูง
ใน Node ควรใช้ `pipeline()` ซึ่งจัดการ propagation ของ error และ cleanup ได้ดีกว่าต่อ `.pipe()` หลายจุดเอง สำหรับระบบจริงควร monitor event-loop lag, heap usage, GC pause, active handles และ latency percentile ไม่ดู CPU อย่างเดียว