3.3Kпросмотров
13.8%от подписчиков
23 марта 2026 г.
Score: 3.7K
🛠 Конкурентность без бойлерплейта Когда нужно запустить несколько горутин, дождаться всех и обработать первую ошибку, то руки обычно тянутся к sync.WaitGroup плюс канал для ошибок. Это легко сделать неправильно: пропустить ошибку, получить дедлок, написать двадцать строк там, где достаточно пяти. Давайте попробуем использовать errgroup. g.Go запускает горутину. g.Wait ждёт все и возвращает первую ненулевую ошибку. Если любая горутина вернула ошибку — производный ctx отменяется, остальные горутины, которые слушают контекст, завершаются досрочно:
func fetchAll(ctx context.Context, ids []string) ([]Result, error) { g, ctx := errgroup.WithContext(ctx) results := make([]Result, len(ids)) for i, id := range ids { i, id := i, id // захватываем переменные цикла g.Go(func() error { r, err := fetch(ctx, id) if err != nil { return err } results[i] = r return nil }) } if err := g.Wait(); err != nil { return nil, err } return results, nil
} Если нужно сделать 200 запросов, но не более 10 одновременно — errgroup хорошо компонуется с семафором:
sem := semaphore.NewWeighted(10) g.Go(func() error { if err := sem.Acquire(ctx, 1); err != nil { return err } defer sem.Release(1) return doWork(ctx)
}) С sync.WaitGroup и каналом вам нужно самостоятельно: не забыть wg.Add, не забыть wg.Done в defer, правильно буферизировать канал ошибок, не заблокироваться на записи в него, вручную отменять контекст при ошибке. errgroup делает всё это за вас и при этом хорошо компонуется с таймаутами и отменой. 📍 Навигация: Вакансии • Задачи • Собесы 🐸 Библиотека Go-разработчика #GoDeep