不好的处理错误方式,或者说 没有进行错误处理的例子:
func main() {
checkStatus := func(done <-chan interface{}, urls ...string) <-chan *http.Response{
responses := make(chan *http.Response)
go func() {
defer close(responses)
for _, url := range urls{
resp, err := http.Get(url)
if err != nil {
fmt.Println(err)
continue
}
select{
case <- done :
return
case responses <- resp:
}
}
}()
return responses
}
done := make(chan interface{})
defer close(done)
urls := []string{"https://www.baidu.com", "https://badhost"}
for response := range checkStatus(done, urls...) {
fmt.Printf("Response: %v\n", response.Status)
}
}
*/
//Response: 200 OK
//Get https://badhost: dial tcp: lookup badhost: no such host
建议: 一般来说,你的并发进程应该把它们的错误发送到你的程序的另一个部分,他有你的程序状态的完整信息,并可以作出更明智的决定。
下面是正确的解决方案:
func main(){
type Result struct{
Error error
Response *http.Response
}
checkStatus := func(done <-chan interface{}, urls ...string) <-chan Result {
results := make(chan Result)
go func() {
defer close(results)
for _, url := range urls{
var result Result
resp, err := http.Get(url)
result = Result{Error:err, Response:resp}
select{
case <-done :
return
case results <- result :
}
}
}()
return results
}
done := make(chan interface{})
defer close(done)
urls := []string{"https://www.baidu.com", "https://badhost"}
for result := range checkStatus(done, urls...){
if result.Error != nil {
fmt.Printf("error:%v", result.Error)
continue
}
fmt.Printf("Response:%v \n", result.Response.Status)
}
}
分析: 已经成功地将 错误处理的担忧从 生产者goroutine 中分离出来。这是可取的!对的!。
因为goroutine 的goroutine (在这里就是 main goroutine了)具有更多 关于运行的程序上下文,
并且可以做出关于如何处理错误的更明智的决定。
上面例子; 还可以稍微修改程序 ,可以做其他的事。如: 在 出现三个或更多错误时,停止 checkStatus
在构建从goroutine 返回值时,应将错误视为一等公民。 如果你的goroutine可能产生错误,那么这些错误应该与你的结果紧密结合起来,
并且通过相同的通信 传递。