package err_group import ( "context" "math/rand" "time" "golang.org/x/sync/errgroup" ) type WeatherData struct { City string Weather string Temp int } var WeatherList = []string{"晴", "阴", "多云", "小雨", "大雨"} // fetchWeatherData 获取天气数据 func fetchWeatherData(ctx context.Context, city string) (*WeatherData, error) { // 这里仅为示例,实际中这里会是 API 调用 // 为了模拟可能出现的超时或错误情况,这里随机休眠一段时间 delay := time.Duration(rand.Intn(500)) * time.Millisecond select { case <-ctx.Done(): return nil, ctx.Err() case <-time.After(delay): } return &WeatherData{ City: city, // 随机天气 Weather: WeatherList[rand.Intn(len(WeatherList))], // 随机温度 Temp: rand.Intn(40), }, nil } func GetAllCityWeatherData(timeOut time.Duration) ([]*WeatherData, error) { // 创建一个携带cancel的context ctx, cancel := context.WithTimeout(context.Background(), timeOut) defer cancel() // 确保所有路径都调用cancel // 初始化一个ErrGroup对象 g, ctx := errgroup.WithContext(ctx) // 城市列表 cities := []string{"New York", "Tokyo", "Berlin", "Paris", "Beijing"} list := make([]*WeatherData, len(cities)) // 循环启动goroutine来获取每个城市的天气数据 for i, city := range cities { g.Go(func() error { // FetchWeatherData使用了上下文,如果上下文被取消,它应该立刻尝试返回 data, err := fetchWeatherData(ctx, city) if err != nil { return err } // 每个协程写入固定下标,避免并发 append 数据竞争 list[i] = data return nil }) } // 等待所有goroutine完成或出现错误 if err := g.Wait(); err != nil { return nil, err } return list, nil }