Golang 有必要實現 async-await 嗎?

前言

今天在某站上面看到一個大佬解釋 Golang 中的錯誤處理 err !=nil 時,直接用 Javascript 的 async/await 來解釋。async/await 語法糖在 C#, Python 和 Javascript 中是很常見的異步協程寫法,而在 Golang 中則是使用 goroutine 機制。這時習慣或者喜歡 async/await 語法糖的人可能就會有疑問:Golang 有必要實現 async/await 嗎?

async/await 的出現

最初大家寫請求是這樣寫的:

res = http.get("a.com")
res = http.get("b.com")

這樣順序執行,請求速度很慢。

於是有人寫了線程池,支持並行發請求,api 是 callback 風格:

http.get("a.com", function(err,res){
    //xxx
})
http.get("b.com", function(err,res){
    //xxx
})

但是很快代碼就變成了:

function(){
  // xx
  http.get("a.com", function(err, res){
    // xx
    bar(function(){
        // xx
        foo(function(){
          // xx
          http.post("b.com"function(err, res){
            // xx
          })
        })
      })
  })
}

這樣閱讀性非常低

我們希望異步代碼長得像同步代碼,於是又改成:

await http.get("a.com")
await bar()
await foo()
await http.post("b.com")

爲啥總要寫個 await 呢,是否能去掉呢?

http.get("a.com")
bar()
foo()
http.post("b.com")

這樣就又回到了一開始

因爲光有 await 是沒用的,一定要搭配 Promise.all 纔有用:

res1, res2 = await Promise.all(http.get("a.com"), http.get("b.com"))
await bar()

這樣可讀性就變高了

Golang Channel 實現

回到 Golang,同樣實現以上功能:

urls := []string{
  "a.com",
  "b.com"
}
resChan := make(chan *http.Response)
for _, url := range urls{
        go func(url string){
            res, _ := http.Get(url)
            resChan <- res
        }(url)
}
for _, _ = range urls{
        res := <- resChan
        fmt.Println(res)
}

這樣看來 channel 完全取代了 Promise,所以 Golang 並不需要 async / await。

Golang await 語義的寫法

寫法很多,但是無一例外,所有的阻塞動作都會被調度器截獲,然後把線程資源釋放出去

官方推薦的 channal

ch := make(chan struct{})
x := 0
go func() {
    defer close(ch)
    x = 1
  }()
<-ch // await
fmt.Println(x)

Mutex

x := 0
mx := sync.Mutex{}
mx.Lock()
go func(){
    defer mx.Unlock()
    x = 1
}()
mx.Lock() // await
fmt.Println(x)

WaitGroup

x := 0
wg := sync.WaitGroup{}
wg.Add(1)
go func(){
    defer wg.Done()
    x = 1
}()
wg.Wait() // await
fmt.Println(x)

Cond

x := 0
mx := sync.Mutex{}
cond := sync.NewCond(&mx)
go func(){
    defer cond.Signal()
    x = 1
}()
mx.Lock()
cond.Wait() // await
mx.Unlock()
fmt.Println(x)
本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源https://mp.weixin.qq.com/s/WITYzHWZBlfz4ZE86jtxUw