Callback และ Callback Hell
ทำความเข้าใจการทำงานแบบอะซิงโครนัสที่ใช้ callback ระบุปัญหาการซ้อนกันที่เรียกว่า callback hell และเรียนรู้ว่าเหตุใดจึงมีการนำ Promise มาใช้แก้ปัญหานี้
Callback และ Callback Hell เป็นบทเรียน Frontend Academy ฟรีบน CoddyKit นี่คือบทเรียนที่ 2 จากทั้งหมด 4 บทเรียน คุณสามารถอ่านบทเรียนทั้งหมดด้านล่างฟรี — จากนั้นลองปฏิบัติด้วยตัวคุณเองในเบราว์เซอร์พร้อมตัวแก้ไขโค้ดในตัวและติวเตอร์ AI ตลอด 24/7 บทเรียนนี้เป็นส่วนหนึ่งของเส้นทางการเรียน Frontend Academy และความก้าวหน้าของคุณจะซิงค์ข้ามเว็บและแอป CoddyKit คอร์ส Frontend Academy มีบทเรียนทั้งหมด 4 บทเรียน
ฟังก์ชันเรียกกลับคืออะไร
ฟังก์ชันเรียกกลับคือฟังก์ชันที่ส่งเป็นอาร์กิวเมนต์ให้ฟังก์ชันอื่น เพื่อให้ฟังก์ชันนั้นเรียกใช้ภายหลังเมื่องานบางอย่างเสร็จสิ้น ฟังก์ชันเรียกกลับเป็นรูปแบบการทำงานแบบอะซิงก์ดั้งเดิมของ JavaScript ซึ่งมีมาก่อนพรอมิส
// Synchronous callback:
[1, 2, 3].forEach(function(n) {
console.log(n);
});
// Async callback:
setTimeout(function() {
console.log('1 second later');
}, 1000);ฟังก์ชันเรียกกลับแบบข้อผิดพลาดเป็นอาร์กิวเมนต์แรกใน Node.js
Node.js ทำให้รูปแบบหนึ่งเป็นมาตรฐาน นั่นคือ ฟังก์ชันเรียกกลับจะรับข้อผิดพลาดเป็นอาร์กิวเมนต์แรกและรับผลลัพธ์เป็นอาร์กิวเมนต์ที่สอง หากค่าแรกเป็น null หรือ undefined แสดงว่าการดำเนินการสำเร็จ
fs.readFile('data.json', 'utf8', function(err, data) {
if (err) {
console.error('Failed to read file:', err);
return;
}
console.log('File contents:', data);
});ขั้นตอนแบบอะซิงก์ด้วยฟังก์ชันเรียกกลับ
เมื่อการดำเนินการแบบอะซิงก์รายการหนึ่งขึ้นอยู่กับผลลัพธ์ของอีกรายการหนึ่ง คุณจะต้องซ้อนฟังก์ชันเรียกกลับ เมื่อมีขั้นตอนต่อเนื่องกันสามขั้น โค้ดก็เริ่มดูยุ่งยากแล้ว
getUser(userId, function(err, user) {
if (err) return handleError(err);
getOrders(user.id, function(err, orders) {
if (err) return handleError(err);
getInvoice(orders[0].id, function(err, invoice) {
if (err) return handleError(err);
// Finally use invoice
renderInvoice(invoice);
});
});
});นรกของฟังก์ชันเรียกกลับ — พีระมิดแห่งหายนะ
การซ้อนกันลึก ๆ เรียกว่า นรกของฟังก์ชันเรียกกลับ หรือ พีระมิดแห่งหายนะ ปัญหาที่เกิดขึ้นคือ โค้ดจะเลื่อนไปทางขวาเพิ่มขึ้นในทุกระดับ การจัดการข้อผิดพลาดซ้ำซาก ลำดับการทำงานติดตามได้ยาก และการปรับโครงสร้างโค้ดทำได้อย่างยุ่งยาก
ปัญหาของนรกฟังก์ชันเรียกกลับ
1) การจัดการข้อผิดพลาดต้องตรวจสอบข้อผิดพลาดอย่างชัดเจนในทุกระดับ 2) การแก้จุดบกพร่องทำได้ยาก เพราะร่องรอยสแตกไม่แสดงลำดับการทำงานเชิงตรรกะ 3) โค้ดอ่านยากเนื่องจากแบ่งเป็นชั้นในแนวนอน 4) อาจพลาดข้อผิดพลาดได้ง่ายโดยไม่ตั้งใจ
วิธีลดปัญหา: ฟังก์ชันที่มีชื่อ
แยกฟังก์ชันเรียกกลับที่ซ้อนกันออกมาเป็นฟังก์ชันระดับบนสุดที่มีชื่อ วิธีนี้ทำให้การซ้อนกันลดลงและทำให้สามารถทดสอบแต่ละขั้นแยกจากกันได้ แต่ยังไม่สามารถแก้ปัญหาพื้นฐานด้านการประสานงานได้
function onInvoice(err, invoice) {
if (err) return handleError(err);
renderInvoice(invoice);
}
function onOrders(err, orders) {
if (err) return handleError(err);
getInvoice(orders[0].id, onInvoice);
}
function onUser(err, user) {
if (err) return handleError(err);
getOrders(user.id, onOrders);
}
getUser(userId, onUser);คลังโปรแกรมควบคุมลำดับการทำงาน (ในอดีต)
คลังโปรแกรมอย่าง async.js มีตัวช่วย เช่น async.waterfall และ async.parallel สำหรับจัดการลำดับการทำงานที่อาศัยฟังก์ชันเรียกกลับ คลังเหล่านี้เป็นแนวทางที่ทันสมัยที่สุดก่อนที่พรอมิสจะมาถึงใน ES2015
เหตุใดฟังก์ชันเรียกกลับจึงยังไม่หายไป
ฟังก์ชันเรียกกลับยังคงปรากฏอยู่ทั่วไป ตัวฟังเหตุการณ์ เมธอดของอาร์เรย์ setTimeout และ API ของสตรีมล้วนใช้ฟังก์ชันเรียกกลับ ประเด็นสำคัญคือ สำหรับการจัดลำดับงานแบบอะซิงก์ พรอมิสและการทำงานแบบอะซิงก์/รอผลเหมาะสมกว่า ส่วนฟังก์ชันเรียกกลับยังเหมาะสำหรับเหตุการณ์ที่เกิดขึ้นครั้งเดียวและเรียบง่าย
แปลงฟังก์ชันเรียกกลับเป็นพรอมิส: การทำให้เป็นพรอมิส
util.promisify() ของ Node.js จะห่อฟังก์ชันเรียกกลับแบบข้อผิดพลาดเป็นอาร์กิวเมนต์แรก ให้กลายเป็นเวอร์ชันที่คืนค่าพรอมิส วิธีนี้เชื่อมต่อ API รุ่นเก่ากับโค้ดการทำงานแบบอะซิงก์/รอผลสมัยใหม่
const { promisify } = require('util');
const readFile = promisify(require('fs').readFile);
async function readConfig() {
const data = await readFile('config.json', 'utf8');
return JSON.parse(data);
}เมื่อใดที่ฟังก์ชันเรียกกลับยังเป็นเครื่องมือที่เหมาะสม
ตัวฟังเหตุการณ์ (addEventListener) ต้องการฟังก์ชันเรียกกลับ ซึ่งเหมาะสมแล้ว เพราะตัวฟังเหตุการณ์จะทำงานหลายครั้ง ตัวจัดการข้อมูลของสตรีมก็ต้องการฟังก์ชันเรียกกลับเช่นกัน พรอมิสให้ผลลัพธ์แบบอะซิงก์ครั้งเดียว ส่วนฟังก์ชันเรียกกลับใช้สำหรับการสมัครรับเหตุการณ์หลายครั้ง
ก้าวต่อไป: พรอมิสและการทำงานแบบอะซิงก์/รอผล
พรอมิสที่เพิ่มใน ES2015 และรูปแบบการทำงานแบบอะซิงก์/รอผลใน ES2017 ช่วยแก้ปัญหานรกของฟังก์ชันเรียกกลับ โค้ด JavaScript สมัยใหม่แทบจะใช้การทำงานแบบอะซิงก์/รอผลสำหรับการดำเนินการแบบอะซิงก์ที่ต่อเนื่องกันเสมอ การทำความเข้าใจฟังก์ชันเรียกกลับยังคงสำคัญต่อการอ่านโค้ดเก่าและโค้ดภายในของคลังโปรแกรม
ตรวจสอบความเข้าใจ
ปัญหาหลักที่คำว่า 'นรกของฟังก์ชันเรียกกลับ' อธิบายไว้คืออะไร
ทบทวน: ฟังก์ชันเรียกกลับ
ฟังก์ชันเรียกกลับคือฟังก์ชันที่ส่งให้ฟังก์ชันอื่นเพื่อให้เรียกใช้ภายหลัง นี่คือรูปแบบการทำงานแบบอะซิงก์ดั้งเดิม ฟังก์ชันเรียกกลับแบบข้อผิดพลาดเป็นอาร์กิวเมนต์แรก (ข้อผิดพลาด, ผลลัพธ์) เป็นรูปแบบมาตรฐานของ Node.js การซ้อนกันลึก ๆ ทำให้เกิดนรกของฟังก์ชันเรียกกลับ การแยกฟังก์ชันที่มีชื่อช่วยลดการซ้อนกัน ใช้พรอมิสหรือการทำงานแบบอะซิงก์/รอผลสำหรับลำดับการทำงานแบบอะซิงก์ที่ต่อเนื่องกัน ฟังก์ชันเรียกกลับยังคงเหมาะสมสำหรับตัวฟังเหตุการณ์และสตรีม
คำถามที่พบบ่อย
บทเรียน “Callback และ Callback Hell” ฟรีหรือไม่
ใช่ — ข้อความเต็มของ “Callback และ Callback Hell” ฟรีให้อ่านที่นี่บนเว็บ เพื่อปฏิบัติแบบโต้ตอบ (ตัวแก้ไขโค้ดในตัวและติวเตอร์ AI ตลอด 24/7) และปลดล็อคส่วนที่เหลือของคอร์ส Frontend Academy ให้อัปเกรดเป็น CoddyKit PRO คอร์ส Frontend Academy มีบทเรียนทั้งหมด 4 บทเรียน
คุณจะเรียนรู้อะไรในบทเรียน “Callback และ Callback Hell”
ทำความเข้าใจการทำงานแบบอะซิงโครนัสที่ใช้ callback ระบุปัญหาการซ้อนกันที่เรียกว่า callback hell และเรียนรู้ว่าเหตุใดจึงมีการนำ Promise มาใช้แก้ปัญหานี้ คุณปฏิบัติ Frontend Academy ด้วยโค้ดที่ใช้งานได้จริงที่คุณเรียกใช้โดยตรงในเบราว์เซอร์ และติวเตอร์ AI ตลอด 24/7 ตอบคำถามของคุณขณะที่คุณไปผ่านบทเรียน
คุณต้องมีประสบการณ์ก่อนที่จะเริ่มเรียน Frontend Academy หรือไม่
ไม่จำเป็นต้องมีประสบการณ์มาก่อน Frontend Academy บน CoddyKit ออกแบบมาสำหรับผู้เริ่มต้นไปจนถึงผู้เรียนขั้นสูง คุณสามารถเริ่มต้นที่นี่หรือเริ่มจากตัวแรกและเรียนด้วยความเร็วของคุณเอง นี่คือบทเรียนที่ 2 จากทั้งหมด 4 บทเรียน
บทเรียน “Callback และ Callback Hell” ใช้เวลานานแค่ไหน
บทเรียน CoddyKit ส่วนใหญ่ใช้เวลาประมาณ 5–10 นาที แต่ละบทเรียนจึงสั้นและเป็นแบบโต้ตอบ คุณสามารถก้าวหน้าอย่างต่อเนื่องและกลับมาเรียนต่อจากตรงที่เพิ่งหยุดบนเว็บและแอปได้เลย
ฉันเขียนและรันโค้ดในบทเรียน Frontend Academy นี้ได้ไหม
ได้ บทเรียน Frontend Academy ทุกบทมีตัวแก้ไขโค้ดในตัว คุณจึงเขียนและรันโค้ดจริงได้เลยในเบราว์เซอร์ และได้รับข้อเสนอแนะจาก AI ในทันที — ไม่ต้องติดตั้งในเครื่องของคุณ
บทเรียนทั้งหมดในหลักสูตรนี้
- Event Loop: Call Stack, Queue และ Microtasks
- Callback และ Callback Hell
- Promise: then, catch, finally และ Promise.all
- async/await และการจัดการข้อผิดพลาด