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

xpcall และตัวจัดการข้อความ

ใช้ xpcall ร่วมกับตัวจัดการแบบกำหนดเองเพื่อดูร่องรอยข้อผิดพลาดโดยละเอียด

xpcall และตัวจัดการข้อความ เป็นบทเรียน Lua Academy ฟรีบน CoddyKit นี่คือบทเรียนที่ 3 จากทั้งหมด 4 บทเรียน คุณสามารถอ่านบทเรียนทั้งหมดด้านล่างฟรี — จากนั้นลองปฏิบัติด้วยตัวคุณเองในเบราว์เซอร์พร้อมตัวแก้ไขโค้ดในตัวและติวเตอร์ AI ตลอด 24/7 บทเรียนนี้เป็นส่วนหนึ่งของเส้นทางการเรียน Lua Academy และความก้าวหน้าของคุณจะซิงค์ข้ามเว็บและแอป CoddyKit คอร์ส Lua Academy มีบทเรียนทั้งหมด 4 บทเรียน

ไวยากรณ์ของ xpcall

xpcall(f, handler, ...) เรียก f พร้อมอาร์กิวเมนต์ ... ในโหมดที่มีการป้องกัน และเมื่อเกิดข้อผิดพลาดจะเรียก handler(errorObject) ค่าที่ตัวจัดการคืนมาจะกลายเป็นค่าที่คืนค่าที่สองของ xpcall ต่างจาก pcall ตรงที่ xpcall เรียกตัวจัดการขณะที่ลำดับการเรียกยังสมบูรณ์

local function handler(err)
  return "HANDLED: " .. tostring(err)
end

local ok, msg = xpcall(
  function() error("something bad") end,
  handler
)

print(ok)   -- false
print(msg)  -- HANDLED: ...: something bad

ใช้ debug.traceback เป็นตัวจัดการ

ตัวจัดการของ xpcall ที่ใช้กันทั่วไปที่สุดคือ debug.traceback ให้ส่งเป็นตัวจัดการโดยตรง เพราะจะจัดรูปแบบข้อผิดพลาดพร้อมร่องรอยการเรียกทั้งหมด ซึ่งมีประโยชน์อย่างมากต่อการแก้ไขข้อผิดพลาดในระบบจริง

local function level3() error("deep error") end
local function level2() level3() end
local function level1() level2() end

local ok, err = xpcall(level1, debug.traceback)

if not ok then
  -- err contains the full stack trace
  print(err)
end

ตัวจัดการแบบกำหนดเองพร้อมบริบท

ตัวจัดการแบบกำหนดเองสามารถเพิ่มบริบทได้ เช่น ประทับเวลา บันทึกลงไฟล์ และแจ้งระบบติดตามตรวจสอบ จากนั้นจึงคืนค่าข้อผิดพลาดที่จัดรูปแบบแล้ว นี่คือรูปแบบมาตรฐานสำหรับการจัดการข้อผิดพลาดของแอปพลิเคชัน Lua ในระบบจริง

local function errorHandler(err)
  local trace = debug.traceback(err, 2)
  local ts = os.date("%H:%M:%S")
  local report = string.format("[%s] ERROR\n%s", ts, trace)
  -- Could log to file here
  io.stderr:write(report .. "\n")
  return report
end

local ok, msg = xpcall(
  function()
    local t = nil
    return t.field   -- error!
  end,
  errorHandler
)
print("ok:", ok)

ตัวจัดการต้องไม่เกิดข้อผิดพลาด

หากตัวจัดการข้อความเองยกข้อผิดพลาด Lua จะคืนตัวบ่งชี้ข้อผิดพลาดโดยไม่เรียกตัวจัดการซ้ำ ควรเขียนตัวจัดการให้รับมือได้ทุกกรณีเสมอ ห้ามใช้ IO ที่อาจล้มเหลวและห้ามเข้าถึงดัชนีของค่า nil

local function safeHandler(err)
  -- Keep handler simple and safe
  local ok, trace = pcall(debug.traceback, err, 2)
  if ok then return trace
  else return tostring(err) .. " (traceback failed)"
  end
end

local ok, msg = xpcall(
  function() error({complex="error table"}) end,
  safeHandler
)
print(ok, type(msg))

ใช้ xpcall กับลูปหลัก

ในโปรแกรมที่ทำงานต่อเนื่องเป็นเวลานาน เช่น เซิร์ฟเวอร์หรือลูปเกม ให้ห่อฟังก์ชันหลักด้วย xpcall เพื่อดักจับและบันทึกข้อผิดพลาดที่ไม่ได้จัดการโดยไม่ทำให้โปรแกรมหยุดทำงาน จากนั้นลูปหลักจะตัดสินใจได้ว่าจะเริ่มใหม่หรือจบการทำงาน

local function mainApp()
  -- simulate work
  for i = 1, 3 do
    print("Tick", i)
    if i == 2 then error("transient error") end
  end
end

local function handler(e)
  return debug.traceback("App error: "..tostring(e), 2)
end

local ok, err = xpcall(mainApp, handler)
if not ok then
  print("Application crashed:\n" .. err)
end

การรายงานข้อผิดพลาดแบบมีโครงสร้าง

ผสาน xpcall เข้ากับออบเจ็กต์ข้อผิดพลาดแบบมีโครงสร้างและตัวจัดการที่มีข้อมูลครบถ้วน เพื่อสร้างรายงานข้อผิดพลาดที่ละเอียดและนำไปดำเนินการต่อได้ สำหรับการแก้ไขข้อผิดพลาดหรือแผงติดตามตรวจสอบ

local function handler(err)
  local info = {
    error   = tostring(err),
    time    = os.date("!%Y-%m-%dT%H:%M:%SZ"),
    trace   = debug.traceback(nil, 2),
  }
  return info
end

local ok, report = xpcall(
  function() error({code=500, msg="internal error"}) end,
  handler
)

if not ok then
  print("Time:", report.time)
  print("Error:", report.error)
  -- print("Trace:", report.trace)
end

ใช้ xpcall กับโครูทีน

ภายในโครูทีน pcall ทำงานตามปกติ หากต้องการร่องรอยการเรียกจากข้อผิดพลาดภายในโครูทีน ให้ห่อเนื้อหาของโครูทีนด้วย xpcall ตัวจัดการจะทำงานภายในบริบทลำดับการเรียกของโครูทีน

local function co_body()
  error("error inside coroutine")
end

local co = coroutine.create(function()
  local ok, err = xpcall(co_body, debug.traceback)
  if not ok then
    print("Caught in coroutine:", err:match("([^\n]+)"))
  end
end)

coroutine.resume(co)

การเพิ่มข้อมูลให้ออบเจ็กต์ข้อผิดพลาด

ตัวจัดการสามารถเปลี่ยนข้อผิดพลาดแบบสตริงธรรมดาให้เป็นออบเจ็กต์ที่มีข้อมูลครบถ้วน หรือเพิ่มข้อมูลให้ออบเจ็กต์ที่มีข้อมูลอยู่แล้วได้ วิธีนี้ทำให้โค้ดระดับล่างโยนข้อผิดพลาดอย่างง่าย ขณะที่ตัวจัดการเพิ่มบริบท เช่น รหัสคำขอ เซสชันผู้ใช้ และข้อมูลสภาพแวดล้อม

local requestID = "req-123"

local function handler(err)
  if type(err) == "string" then
    return {message=err, requestID=requestID, level="error"}
  end
  err.requestID = requestID
  return err
end

local ok, result = xpcall(
  function() error("database timeout") end,
  handler
)

if not ok then
  print(result.message, result.requestID)
  -- database timeout  req-123
end

เปรียบเทียบ pcall กับ xpcall

ใช้ pcall เมื่อคุณต้องการเพียงค่าข้อผิดพลาด ข้อผิดพลาดนั้นคาดหมายได้และจัดการทันที หรือให้ความสำคัญกับความเรียบง่าย ใช้ xpcall เมื่อคุณต้องการร่องรอยการเรียก อยู่ที่ขอบเขตระดับบนสุด หรือต้องการเพิ่มบริบทให้ข้อผิดพลาดทั้งหมด

-- pcall: simple, no overhead
local ok, err = pcall(function()
  return 1/0   -- no error in Lua! returns inf
end)
print(ok, err)   -- true  inf

-- xpcall: adds traceback
local ok2, err2 = xpcall(
  function() error("real error") end,
  debug.traceback
)
print(ok2)       -- false
print(err2:sub(1,40))  -- first line of traceback

ค่าที่ตัวจัดการคืน

ไม่ว่าตัวจัดการจะคืนค่าอะไร ค่านั้นจะกลายเป็นค่าที่สองจาก xpcall หากตัวจัดการคืนค่า nil ค่าที่คืนค่าที่สองของ xpcall ก็จะเป็น nil ตัวจัดการที่คืนออบเจ็กต์ข้อผิดพลาดเดิมพร้อมข้อมูลเพิ่มเติมเป็นแนวทางที่ยืดหยุ่นที่สุด

local function enrichedHandler(err)
  return {
    original = err,
    traceback = debug.traceback(nil, 2),
    timestamp = os.time(),
  }
end

local ok, report = xpcall(
  function() error("oops") end,
  enrichedHandler
)

if not ok then
  print(type(report))          -- table
  print(report.original)       -- ...: oops
  print(report.timestamp > 0)  -- true
end

ตรวจสอบอย่างรวดเร็ว

ข้อได้เปรียบสำคัญของ xpcall เหนือ pcall คืออะไร

ทบทวน: xpcall

สรุป:

  • xpcall(f, handler, ...) — ตัวจัดการทำงานขณะที่ลำดับการเรียกยังสมบูรณ์
  • ใช้ debug.traceback เป็นตัวจัดการเพื่อรับร่องรอยการเรียกทั้งหมด
  • ตัวจัดการต้องไม่เกิดข้อผิดพลาด ควรเขียนให้เรียบง่าย
  • เพิ่มบริบทให้ข้อผิดพลาดภายในตัวจัดการ
  • ห่อลูปหลักหรือตัวจัดการเซิร์ฟเวอร์ด้วย xpcall
  • ค่าที่ตัวจัดการคืนจะกลายเป็นค่าที่คืนค่าที่สองของ xpcall

คำถามที่พบบ่อย

บทเรียน “xpcall และตัวจัดการข้อความ” ฟรีหรือไม่

ใช่ — ข้อความเต็มของ “xpcall และตัวจัดการข้อความ” ฟรีให้อ่านที่นี่บนเว็บ เพื่อปฏิบัติแบบโต้ตอบ (ตัวแก้ไขโค้ดในตัวและติวเตอร์ AI ตลอด 24/7) และปลดล็อคส่วนที่เหลือของคอร์ส Lua Academy ให้อัปเกรดเป็น CoddyKit PRO คอร์ส Lua Academy มีบทเรียนทั้งหมด 4 บทเรียน

คุณจะเรียนรู้อะไรในบทเรียน “xpcall และตัวจัดการข้อความ”

ใช้ xpcall ร่วมกับตัวจัดการแบบกำหนดเองเพื่อดูร่องรอยข้อผิดพลาดโดยละเอียด คุณปฏิบัติ Lua Academy ด้วยโค้ดที่ใช้งานได้จริงที่คุณเรียกใช้โดยตรงในเบราว์เซอร์ และติวเตอร์ AI ตลอด 24/7 ตอบคำถามของคุณขณะที่คุณไปผ่านบทเรียน

คุณต้องมีประสบการณ์ก่อนที่จะเริ่มเรียน Lua Academy หรือไม่

ไม่จำเป็นต้องมีประสบการณ์มาก่อน Lua Academy บน CoddyKit ออกแบบมาสำหรับผู้เริ่มต้นไปจนถึงผู้เรียนขั้นสูง คุณสามารถเริ่มต้นที่นี่หรือเริ่มจากตัวแรกและเรียนด้วยความเร็วของคุณเอง นี่คือบทเรียนที่ 3 จากทั้งหมด 4 บทเรียน

บทเรียน “xpcall และตัวจัดการข้อความ” ใช้เวลานานแค่ไหน

บทเรียน CoddyKit ส่วนใหญ่ใช้เวลาประมาณ 5–10 นาที แต่ละบทเรียนจึงสั้นและเป็นแบบโต้ตอบ คุณสามารถก้าวหน้าอย่างต่อเนื่องและกลับมาเรียนต่อจากตรงที่เพิ่งหยุดบนเว็บและแอปได้เลย

ฉันเขียนและรันโค้ดในบทเรียน Lua Academy นี้ได้ไหม

ได้ บทเรียน Lua Academy ทุกบทมีตัวแก้ไขโค้ดในตัว คุณจึงเขียนและรันโค้ดจริงได้เลยในเบราว์เซอร์ และได้รับข้อเสนอแนะจาก AI ในทันที — ไม่ต้องติดตั้งในเครื่องของคุณ

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

  1. ฟังก์ชัน error()
  2. การเรียกใช้แบบป้องกันด้วย pcall
  3. xpcall และตัวจัดการข้อความ
  4. ออบเจกต์ข้อผิดพลาดแบบมีโครงสร้าง
← กลับไปที่ Lua Academy