我正在學習 golang 源代碼并陷入 defer 函式執行順序。我有兩個檔案:一個定義端點的行為,另一個用于測驗。我洗掉了一些與我的問題無關的代碼以減少要閱讀的行。端點定義檔案
// 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{}{}
)
func annotate(s string) endpoint.Middleware {
return func(next endpoint.Endpoint) endpoint.Endpoint {
return func(ctx context.Context, request interface{}) (interface{}, error) {
fmt.Println(s, "pre")
defer fmt.Println(s, "post")
return next(ctx, request)
}
}
}
func myEndpoint(context.Context, interface{}) (interface{}, error) {
fmt.Println("my endpoint!")
return struct{}{}, nil
}
以我的理解,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,與secondthird. posts的順序顛倒了。與回傳值endpoint.Chain串列的執行相反,annotate但annotate首先評估方法對嗎?不是說,pres 被列印,這意味著首先執行內部函式
uj5u.com熱心網友回復:
延遲函式作為函式中的最后一件事運行,在 return 陳述句之后,因此annotate函式將首先運行next,只有在回傳之后延遲函式才會運行。根據您的代碼,它應該列印的順序是:
first pre
second pre
third pre
my endpoint
third post
second post
first post
uj5u.com熱心網友回復:
這是你的例子變成了在 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. 但是,如果我們能恐慌之間的fmt.Println(s, "pre")和后段,那么這可能是有意義的。
轉載請註明出處,本文鏈接:https://www.uj5u.com/qianduan/343540.html
標籤:走
上一篇:Go中按new(Type)和&Type{}分配記憶體的區別
下一篇:我無法用自動收報機逃脫for回圈
