【语法算法】
xpcall 的错误处理函数就这一行:
local ok, err = xpcall(fn, function(err) return debug.traceback(err, 2) end)
xpcall 和 pcall 的本质区别就一个:xpcall 可以传入一个错误处理函数——这个函数在错误发生的第一时间被调用——可以拿到完整的调用栈。pcall 只能在错误传播到外层后才拿到错误信息——堆栈信息已经丢失了。今天拆开。
一、传入、捕获、堆栈三段
xpcall 的三段。传入:xpcall 的第二个参数是错误处理函数——这个函数在错误发生的那一帧被调用——不是等错误传播到外层才处理——所以可以拿到完整的调用堆栈。捕获:错误处理函数的返回值就是 xpcall 的第二个返回值——通常返回 debug.traceback(err, 2) 把原始错误和堆栈拼接在一起。堆栈:debug.traceback(err, 2) 从错误的调用位置往外数两层——跳过错误处理函数自身和 xpcall 自身——输出从业务代码开始的完整调用链。三段连起来:xpcall 玩的是"在错误发生的第一现场获取完整上下文"——不是等错误传到外面再看残局。
graph TD
A["xpcall(fn, errHandler)"] --> B{"fn执行成功?"}
B -->|是| C["ok=true"]
B -->|否| D["调用errHandler(err)"]
D --> E["errHandler返回堆栈信息"]
E --> F["ok=false, result=堆栈"]
二、xpcall 的正确写法
local function riskyOperation(data)
if type(data) ~= "table" then
error("data must be table", 2)
end
return data.value * 2
end
local function errorHandler(err)
return debug.traceback(err, 2)
end
local ok, result = xpcall(riskyOperation, errorHandler, { value = 21 })
if ok then
print("结果:", result)
else
print("错误:", result)
end
-- 带额外参数的xpcall
local ok2, result2 = xpcall(riskyOperation, errorHandler, "not_a_table")
if not ok2 then
print("参数错误:", result2)
end
xpcall 的后续参数传给被保护的函数——xpcall(fn, errHandler, arg1, arg2) 等价于 pcall(fn, arg1, arg2) 加上错误处理函数——参数传递的路径和 pcall 一致。
三、堆栈的层次与 errHandler 的纪律
debug.traceback(err, 2) 的第二个参数 2 是堆栈层级——跳过 errHandler 自身和 xpcall 自身——从业务代码的调用处开始打印堆栈——层级不对就会把内部实现细节也打出来。errHandler 的纪律只有一条:不要在 errHandler 里做复杂的事——errHandler 在错误发生的第一帧被调用,此时 Lua 的状态还不稳定——errHandler 里只做字符串拼接和简单格式化,不要调用可能再次报错的函数——errHandler 里再报错就是嵌套错误,Lua 会直接终止程序。
全站技术干货持续更新:996 引擎 / Lua 实战帖,语法、参数与示例一篇讲透。进入文章地图 · 查看全部 →
【游戏功能】 再抛一个老问题:奶量是固定好还是浮动好?固定回五十的术式,满血喝是五十、残血喝也是五十,账目清楚,可总差点意思…
【游戏功能】 前阵子一场"永动机事故"在群里传了半天:某个会喷火的精英怪被玩家围了一整晚,火喷了八个钟头没停——后台一看,它…
【游戏功能】 mode=mode==='red'?'blue':'red' ——先拆这一行。一个变量存着当前纹色,三元运算符…
【游戏功能】 上一版埋了个查了半宿的隐蔽报错:测试组报"玩家血条归零了人还站着",后台的死亡日志一条没有,复活接口也没被调过…
【游戏功能】 先抛一个坑:增益为什么只能自己挂?怒气、护体、神行,攻略里全在教你"什么时候开自己的招",可怪身上明晃晃挂着的…
【游戏功能】 昨天夜里一场线上事故把"反纹"顶上了台面:一个玩家把反纹挂在自己身上站桩挂机,怪的火球过来他没倒,反倒躺下的是…