ข้ามไปยังเนื้อหา

The Call Stack

Call stack เป็นโครงสร้างข้อมูลแบบ LIFO (Last In, First Out) ที่ติดตามฟังก์ชันที่กำลังทำงานอยู่ ทุกครั้งที่ฟังก์ชันถูกเรียก จะมี เฟรม (frame) ใหม่ถูก push ขึ้น stack เมื่อฟังก์ชัน return เฟรมนั้นจะถูก pop ออก

function greet(name) {
return 'Hello, ' + name;
}
function main() {
var msg = greet('Node');
console.log(msg);
}
main();
// Stack ที่จุดสูงสุด: [main] → [main, greet]
// หลัง greet return: [main]
// หลัง main return: []

เอนจินทำงานได้ ทีละเฟรมเท่านั้น ขณะที่เฟรมหนึ่งกำลังทำงาน ไม่มีอย่างอื่นทำงานได้ — ไม่ว่าจะเป็น callback ของ I/O, callback ของ timer หรืออะไรก็ตาม

โค้ด synchronous ทั้งหมดจะทำงานจนเสร็จก่อนที่อะไรในคิว async จะได้ทำงาน ลองดูตัวอย่างการนับถอยหลัง:

function countdown(n) {
if (n <= 0) {
console.log('done');
return;
}
console.log(n);
countdown(n - 1);
}
countdown(5);

การเรียกซ้ำแต่ละครั้ง push เฟรมใหม่ ผลลัพธ์เป็นแบบ deterministic อย่างสมบูรณ์: 5, 4, 3, 2, 1, done

ลองรัน:

JavaScript

ถ้าคุณรันงาน synchronous ที่ยาวนาน — การคำนวณหนัก, ลูปขนาดใหญ่, การอ่านไฟล์แบบ synchronous — call stack จะถูกครอบครองตลอดช่วงเวลานั้น ไม่มีโค้ดอื่นทำงานได้จนกว่า stack จะว่าง

// โค้ดนี้บล็อกราว ~200ms — ไม่มีอะไรเกิดขึ้นได้ในช่วงนี้
var start = Date.now();
while (Date.now() - start < 200) {
// spinning
}
console.log('Finally unblocked');

ในเซิร์ฟเวอร์ นี่หมายความว่าทุก request ที่เข้ามาต้องรอระหว่างที่บล็อก จงทำให้งาน synchronous สั้นเข้าไว้

โครงสร้างข้อมูลแบบใดอธิบายวิธีที่ call stack ประมวลผลการเรียกฟังก์ชัน?
callback ของ setTimeout เกิดอะไรขึ้นขณะที่ลูป synchronous ที่ยาวนานกำลังทำงาน?
เฟรมของฟังก์ชันถูกนำออกจาก call stack เมื่อใด?