0Pricing
Frontend Academy · บทเรียน

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 ในทันที — ไม่ต้องติดตั้งในเครื่องของคุณ

บทเรียนทั้งหมดในหลักสูตรนี้

  1. Event Loop: Call Stack, Queue และ Microtasks
  2. Callback และ Callback Hell
  3. Promise: then, catch, finally และ Promise.all
  4. async/await และการจัดการข้อผิดพลาด
← กลับไปที่ Frontend Academy