延迟函数执行顺序

我正在学习 golang 源代码并陷入延迟函数执行顺序。我有两个文件:一个定义端点的行为,另一个用于测试。我删除了一些与我的问题无关的代码以减少阅读的行数。端点定义文件


// Endpoint is the fundamental building block of servers and clients.

// It represents a single RPC method.

type Endpoint func(ctx context.Context, request interface{}) (response interface{}, err error)


// Middleware is a chainable behavior modifier for endpoints.

type Middleware func(Endpoint) Endpoint


// Chain is a helper function for composing middlewares. Requests will

// traverse them in the order they're declared. That is, the first middleware

// is treated as the outermost middleware.

func Chain(outer Middleware, others ...Middleware) Middleware {

    return func(next Endpoint) Endpoint {

        for i := len(others) - 1; i >= 0; i-- { // reverse

            next = others[i](next)

        }

        return outer(next)

    }

}

测试文件包含打印的步骤。


func ExampleChain() {

    e := endpoint.Chain(

        annotate("first"),

        annotate("second"),

        annotate("third"),

    )(myEndpoint)


    if _, err := e(ctx, req); err != nil {

        panic(err)

    }


    // Output:

    // first pre

    // second pre

    // third pre

    // my endpoint!

    // third post

    // second post

    // first post

}


var (

    ctx = context.Background()

    req = struct{}{}

)



据我了解,这三个annotate方法应该先执行,然后endpoint.Chain方法,myEndpoint最后执行。此外,由于pre首先打印,并且当函数返回“post”时,应该按照defergo doc 中的说明进行操作: A "defer" statement invokes a function whose execution is deferred to the moment the surrounding function returns, either because the surrounding function executed a return statement, reached the end of its function body, or because the corresponding goroutine is panicking.


所以我期望看到的是


    // Output:

    // first pre

    // first post

    // second pre

    // second post

    // third pre

    // third post

    // my endpoint!

简而言之,我的问题是:


为什么first pre后面不跟first post,一样second third。

s的顺序post颠倒了。endpoint.Chain反向执行返回值列表但annotate首先annotate评估方法对吗?不是说,pres 被打印,这意味着首先执行内部函数


守着星空守着你
浏览 96回答 2
2回答

小怪兽爱吃肉

延迟函数作为函数中的最后一件事运行,在 return 语句之后,所以annotate函数将首先运行next,只有在返回之后,延迟函数才会运行。根据您的代码,它应该打印的顺序是:first presecond prethird premy endpointthird postsecond postfirst post

阿晨1998

这是您的示例变成了在 Go 操场上运行的东西。请注意,如果您在给定函数中defer 多次调用,则每个延迟调用都按 LIFO 顺序运行。因此,如果您想使用defer确保您post首先被调用,然后是next操作,请考虑替换:defer fmt.Println(s, "post")next(ctx, request)和:defer next(ctx, request)defer fmt.Println(s, "post)当然,在您的情况下,您想返回什么next返回,这会产生一个小问题。要在实际情况下解决这个问题,您需要一个小函数和一些命名的返回值:defer func() { i, e = next(ctx, request) }()其中i和e是命名的返回值。这是相同的代码变成了一个新示例,其中延迟调用以所需的顺序发生。 在这种情况下,这个例子是相当愚蠢的,因为没有任何恐慌,也没有中间的“危险步骤”,所以我们真正需要的是fmt.Println按顺序执行这两个调用,而不使用defer. 但是,如果我们可以在the和 post 部分之间感到恐慌fmt.Println(s, "pre"),那么这可能是有道理的。
打开App,查看更多内容
随时随地看视频慕课网APP

相关分类

Go