可以使用很多匿名函数吗?
Is it okay to use many anonymous functions?
我喜欢 golang 的一件事是 defer
语句,但是 defer
只适用于 func
范围。
所以,我经常这样使用它
func (s *Something) abc() error {
func() {
s.Lock()
defer s.Unlock()
// don't lock too long
}()
// do something else
if err := func() error {
resp, err := http.Get("https://example.com/api")
if err != nil {
return err
}
defer resp.Body.Close()
if resp.StatusCode != 200 {
return errors.New("Failed to download")
}
var tmp struct {
Error bool `json:"error"`
Result string `json:"result"`
}
if err := json.NewDecoder(resp.Body).Decode(&tmp); err != nil {
return err
}
if tmp.Error {
return errors.New("API return error")
}
s.somedata = tmp.result
return nil
}(); err != nil {
return err
}
func() {
s.Lock()
defer s.Unlock()
// don't lock too long
}()
// do something else
}
基本上,我把它包装成匿名块func
。
这样的用法很常见吗?其他地鼠会滥用这个事实吗?
编辑:澄清
好吧,看来我没解释好,
我想实现两件事
我想实现的是尽可能短的锁住mutex
func() {
s.Lock()
defer s.Unlock()
// don't lock too long
// there is other code here, this func is not an empty func
}()
这个函数里不止一个http.Get,调用完example再说吧。com/api我要调用example。com/api2。我们需要尽快关闭 resp.Body,因此只与该服务器建立一个 TCP 连接。据我所知,如果还有另一个 HTTP 连接尚未关闭,http.Get 将创建另一个 TCP 连接(resp.Body.Close() 未在先前的响应中调用)。
编辑 2:更多说明
第一个和最后一个匿名函数和锁是为了同步缓存。我是基于map[string]string实现缓存的,所以需要sync-ed.
我需要先调用 example.com/api,然后根据响应我需要调用 example.com/api2 或 example.com/api3,然后再调用 previous http连接必须关闭,可以是这样的代码
resp, err := http.Get("https://example.com/api")
if err != nil {
return err
}
if resp.StatusCode != 200 {
resp.Body.Close()
return errors.New("Failed to download")
}
// process the body
resp.Body.Close()
但是你需要显式地写两次resp.Body.Close()
由于您对 Lock
和 Unlock
的匿名函数,您将进入匿名函数、锁定、延迟解锁、离开触发延迟解锁的匿名函数,然后继续 abc()
。开始和结束的匿名函数几乎没用。
你的中间匿名函数对任何语言来说都是糟糕的风格。没有理由不执行代码并响应任何返回的错误。
我会重写这个函数如下:
func (s *Something) abc() error {
s.Lock()
defer s.Unlock()
resp, err := http.Get("https://example.com/api")
if err != nil {
return err
}
defer resp.Body.Close()
if resp.StatusCode != 200 {
return errors.New("Failed to download")
}
// This struct should probably be global
var tmp struct {
Error bool `json:"error"`
Result string `json:"result"`
}
err = json.NewDecoder(resp.Body).Decode(&tmp)
if err != nil {
return err
}
if tmp.Error {
return errors.New("API return error")
}
s.somedata = tmp.result
return nil
}
匿名函数有它们的位置。我将它们用于闭包以及极其简洁的 go 例程。您可能会在此处使用匿名函数的一个地方是记录 resp.Body.Close()
返回的错误,如下所示:
defer func() {
err = resp.Body.Close()
log.Printf("error closing API response body: %s", err)
}()
你的观察是正确的 defer
只适用于函数范围,我认为你 运行 遇到的问题与 defer
的行为无关.
一般来说,[插入意见] 函数越小越好,这样就不需要创建匿名函数来最大限度地利用 defer
。
拆分你的例子,这样的事情可能会更好:
// Public method that does locking orchestration
func (s *Something) PublicDoABC() error {
s.doWorkStart()
if err := s.populateSomeData(); err != nil {
return err
}
s.doWorkEnd()
return nil
}
// setup function with locking
func (s *Something) doWorkStart() {
s.Lock()
defer s.Unlock()
// do setup work here
}
// teardown function with locking
func (s *Something) doWorkEnd() {
s.Lock()
defer s.Unlock()
// do teardown work here
}
// do the actual request
func (s *Something) populateSomeData() error {
resp, err := http.Get("https://example.com/api")
if err != nil {
return err
}
defer resp.Body.Close()
if resp.StatusCode != 200 {
return errors.New("Failed to download")
}
var tmp struct {
Error bool `json:"error"`
Result string `json:"result"`
}
if err := json.NewDecoder(resp.Body).Decode(&tmp); err != nil {
return err
}
if tmp.Error {
return errors.New("API return error")
}
s.somedata = tmp.Result
return nil
}
我知道这会改变一些事情,你可能不想拆分函数体,你可以在 Something
上定义一个方法,允许通过锁定执行任意函数:
func (s *Something) PublicDoABC() error {
s.executeLocked(func() {
// do setup work
})
if err := s.populateSomeData(); err != nil {
return err
}
s.executeLocked(func() {
// do teardown work
})
return nil
}
// this function allows defer unlocks and unifies locking code
func (s *Something) executeLocked(f func()) {
s.Lock()
defer s.Unlock()
f()
}
回答原问题:不,我不认为这是常见的(具有多个内联匿名函数)。如果感觉不对,几乎可以肯定有更好的方法。
我喜欢 golang 的一件事是 defer
语句,但是 defer
只适用于 func
范围。
所以,我经常这样使用它
func (s *Something) abc() error {
func() {
s.Lock()
defer s.Unlock()
// don't lock too long
}()
// do something else
if err := func() error {
resp, err := http.Get("https://example.com/api")
if err != nil {
return err
}
defer resp.Body.Close()
if resp.StatusCode != 200 {
return errors.New("Failed to download")
}
var tmp struct {
Error bool `json:"error"`
Result string `json:"result"`
}
if err := json.NewDecoder(resp.Body).Decode(&tmp); err != nil {
return err
}
if tmp.Error {
return errors.New("API return error")
}
s.somedata = tmp.result
return nil
}(); err != nil {
return err
}
func() {
s.Lock()
defer s.Unlock()
// don't lock too long
}()
// do something else
}
基本上,我把它包装成匿名块func
。
这样的用法很常见吗?其他地鼠会滥用这个事实吗?
编辑:澄清
好吧,看来我没解释好, 我想实现两件事
我想实现的是尽可能短的锁住mutex
func() { s.Lock() defer s.Unlock() // don't lock too long // there is other code here, this func is not an empty func }()
这个函数里不止一个http.Get,调用完example再说吧。com/api我要调用example。com/api2。我们需要尽快关闭 resp.Body,因此只与该服务器建立一个 TCP 连接。据我所知,如果还有另一个 HTTP 连接尚未关闭,http.Get 将创建另一个 TCP 连接(resp.Body.Close() 未在先前的响应中调用)。
编辑 2:更多说明
第一个和最后一个匿名函数和锁是为了同步缓存。我是基于map[string]string实现缓存的,所以需要sync-ed.
我需要先调用 example.com/api,然后根据响应我需要调用 example.com/api2 或 example.com/api3,然后再调用 previous http连接必须关闭,可以是这样的代码
resp, err := http.Get("https://example.com/api")
if err != nil {
return err
}
if resp.StatusCode != 200 {
resp.Body.Close()
return errors.New("Failed to download")
}
// process the body
resp.Body.Close()
但是你需要显式地写两次resp.Body.Close()
由于您对 Lock
和 Unlock
的匿名函数,您将进入匿名函数、锁定、延迟解锁、离开触发延迟解锁的匿名函数,然后继续 abc()
。开始和结束的匿名函数几乎没用。
你的中间匿名函数对任何语言来说都是糟糕的风格。没有理由不执行代码并响应任何返回的错误。
我会重写这个函数如下:
func (s *Something) abc() error {
s.Lock()
defer s.Unlock()
resp, err := http.Get("https://example.com/api")
if err != nil {
return err
}
defer resp.Body.Close()
if resp.StatusCode != 200 {
return errors.New("Failed to download")
}
// This struct should probably be global
var tmp struct {
Error bool `json:"error"`
Result string `json:"result"`
}
err = json.NewDecoder(resp.Body).Decode(&tmp)
if err != nil {
return err
}
if tmp.Error {
return errors.New("API return error")
}
s.somedata = tmp.result
return nil
}
匿名函数有它们的位置。我将它们用于闭包以及极其简洁的 go 例程。您可能会在此处使用匿名函数的一个地方是记录 resp.Body.Close()
返回的错误,如下所示:
defer func() {
err = resp.Body.Close()
log.Printf("error closing API response body: %s", err)
}()
你的观察是正确的 defer
只适用于函数范围,我认为你 运行 遇到的问题与 defer
的行为无关.
一般来说,[插入意见] 函数越小越好,这样就不需要创建匿名函数来最大限度地利用 defer
。
拆分你的例子,这样的事情可能会更好:
// Public method that does locking orchestration
func (s *Something) PublicDoABC() error {
s.doWorkStart()
if err := s.populateSomeData(); err != nil {
return err
}
s.doWorkEnd()
return nil
}
// setup function with locking
func (s *Something) doWorkStart() {
s.Lock()
defer s.Unlock()
// do setup work here
}
// teardown function with locking
func (s *Something) doWorkEnd() {
s.Lock()
defer s.Unlock()
// do teardown work here
}
// do the actual request
func (s *Something) populateSomeData() error {
resp, err := http.Get("https://example.com/api")
if err != nil {
return err
}
defer resp.Body.Close()
if resp.StatusCode != 200 {
return errors.New("Failed to download")
}
var tmp struct {
Error bool `json:"error"`
Result string `json:"result"`
}
if err := json.NewDecoder(resp.Body).Decode(&tmp); err != nil {
return err
}
if tmp.Error {
return errors.New("API return error")
}
s.somedata = tmp.Result
return nil
}
我知道这会改变一些事情,你可能不想拆分函数体,你可以在 Something
上定义一个方法,允许通过锁定执行任意函数:
func (s *Something) PublicDoABC() error {
s.executeLocked(func() {
// do setup work
})
if err := s.populateSomeData(); err != nil {
return err
}
s.executeLocked(func() {
// do teardown work
})
return nil
}
// this function allows defer unlocks and unifies locking code
func (s *Something) executeLocked(f func()) {
s.Lock()
defer s.Unlock()
f()
}
回答原问题:不,我不认为这是常见的(具有多个内联匿名函数)。如果感觉不对,几乎可以肯定有更好的方法。