为什么当 annoymouse 函数返回时 defer 语句的工作方式不同

why defer statement works differently when annoymouse function returned

这里我声明了一个延迟trace1的函数

func TestDeferFunc(t *testing.T) {
    fmt.Println("start", time.Now())
    defer trace1()
    time.Sleep(3 * time.Second)
}

func trace1() {
    startTime := time.Now()
    fmt.Println("end time: ", startTime)
    fmt.Println("execute time: ", time.Since(startTime))
}

在 运行 go test -run=^TestDeferFunc$ 之后,下面是我得到的

start 2019-11-26 12:50:59.59489797 +0800 CST m=+0.000202866
end time:  2019-11-26 12:51:02.595090951 +0800 CST m=+3.000395880
execute time:  49.065µs

然而,当我推迟另一个烦人的功能时,事情发生了变化

func TestDeferFunc(t *testing.T) {
    fmt.Println("start", time.Now())
    defer trace2()()
    time.Sleep(3 * time.Second)
}

func trace2() func() {
    startTime := time.Now()
    fmt.Println("end time: ", startTime)
    fmt.Println("execute time: ", time.Since(startTime))
    return func() {
        fmt.Println("zzz")
    }
}

下面是 go test 结果

start 2019-11-26 12:52:58.318472958 +0800 CST m=+0.000197852
end time:  2019-11-26 12:52:58.318554368 +0800 CST m=+0.000279262
execute time:  4.853µs
zzz

有人能帮帮我吗!谢谢

这是因为 defer 语句仅 defer 评估函数调用 - 并且函数调用在 defer 执行时评估。根据文档:

Each time a "defer" statement executes, the function value and parameters to the call are evaluated as usual and saved anew but the actual function is not invoked. Instead, deferred functions are invoked immediately before the surrounding function returns, in the reverse order they were deferred. That is, if the surrounding function returns through an explicit return statement, deferred functions are executed after any result parameters are set by that return statement but before the function returns to its caller. If a deferred function value evaluates to nil, execution panics when the function is invoked, not when the "defer" statement is executed.

您的代码 defer trace2()() 本质上等同于 f := trace2(); defer f()。所以 trace2 立即被评估(并因此被调用)。

因此,为了实现您可能想要的(跟踪时间),您可以像这样使用 defer trace3()()trace3()

func trace3() func() {
    startTime := time.Now()

    return func() {
        fmt.Println("end time: ", time.Now())
        fmt.Println("execute time: ", time.Since(startTime))
    }
}

使用 defer 跟踪时间的方法是将开始时间传递给 deferred 函数:

func trace1(startTime time.Time) {
    fmt.Println("start", startTime)
    fmt.Println("end time: ", time.Now())
    fmt.Println("execute time: ", time.Since(startTime))
}

func TestDeferFunc(t *testing.T) {
    defer trace1(time.Now())
    time.Sleep(3 * time.Second)
}

传入的参数立即执行,但真正的函数调用延迟到最后。

如果您想更明确地了解执行的内容和延迟的内容,您可以将整个事情包装在一个函数中,这样您就可以确保内部的所有内容都在最后执行

defer func() {
    ..... code ....
}()