defer 在函数返回前按后进先出顺序执行,参数在 defer 语句出现时求值;recover 必须在 defer 函数中直接调用且仅对同 goroutine 中未结束的 panic 有效。
defer 语句的执行时机和栈顺序
defer 不是“异常时才执行”,而是函数返回前(包括正常 return、panic 中断、甚至函数末尾隐式返回)按后进先出(LIFO)顺序执行。很多人误以为 defer 只用于清理资源,其实它更核心的作用是“确定性地绑定执行时机”。
多个
语句会压入栈,最后定义的最先执行,比如:
和
会输出
再
defer 表达式中的参数在 defer 语句出现时就求值(不是执行时),所以
输出的是
如果想捕获变量的“执行时值”,需用匿名函数包裹:
recover 必须在 defer 函数中直接调用才有效
只有在 panic 正在发生、且当前 goroutine 的 defer 链正在执行时才有意义;一旦 panic 结束或离开 defer 函数体,
就返回 nil,起不到作用。
以下写法无效:
—— 因为
是函数调用,不是函数值,且参数求值发生在 defer 注册时,此时 panic 还没发生
正确写法必须是:
recover 对应的 panic 必须发生在同一 goroutine;跨 goroutine 的 panic 无法被另一个 goroutine 的 defer/recover 捕获
defer + recover 不能捕获所有“错误”,只对 panic 有效
Golang 没有传统 try/catch 异常机制,
值是普通返回值,必须显式检查;
完全不介入
流程。混淆这两者是常见误区。
可被
拦截;
则完全无关
标准库
中绝大多数函数(如
、
)都返回
,不是 panic,不该也不需要 recover
滥用 recover 试图兜底 error,会导致逻辑混乱、错误被静默吞掉、堆栈丢失
实际场景中 defer/recover 的合理用法
真正适合 recover 的场景极少:通常是顶层 goroutine(如 HTTP handler、goroutine 入口)防止 panic 导致整个服务崩溃,并记录日志;其他地方应优先用 error 返回 + 显式判断。
php版微信js-sdk支付接口类
php版微信js-sdk支付接口类
下载
立即学习
“
go语言免费学习笔记(深入)
”;
HTTP handler 示例:
,避免单个请求 panic 杀死整个 server
不建议在工具函数里加 recover,比如封装一个
函数,它该返回
,而不是内部 recover 后返回零值
defer 本身开销极小,但大量 defer(如循环内)可能影响性能;不过比起 panic/recover 的代价,defer 本身几乎可忽略
实际写代码时,最易被忽略的是:recover 的
作用域
严格受限于 defer 函数体 + 同一 goroutine + panic 尚未结束。一旦跳出这个三角约束,recover 就彻底失效——这不是 bug,是设计使然。
deferdefer fmt.Println("a")defer fmt.Println("b")bavar i = 0; defer fmt.Println(i); i = 10defer func(v int) { fmt.Println(v) }(i)recover()recover()defer recover()recover()defer func() { if r := recover(); r != nil { /* 处理 */ } }()errorrecovererrorpanic("network timeout")recoverreturn errors.New("timeout")json.Unmarshalos.Openerrordefer func() { if r := recover(); r != nil { log.Printf("panic: %v", r) } }()ParseJSONerror