Skip to content

All

func All[T any](ctx context.Context, fns ...AsyncFunc[T]) (results []T, errs []error)

All runs every function concurrently and returns when the last one does. Use it to fan out independent work, such as several requests or lookups, and collect every answer.

prices, errs := gollback.All(ctx,
func(ctx context.Context) (Price, error) { return shopA.Price(ctx, sku) },
func(ctx context.Context) (Price, error) { return shopB.Price(ctx, sku) },
func(ctx context.Context) (Price, error) { return shopC.Price(ctx, sku) },
)

results and errs have one entry per function, in the order you passed them: results[i] and errs[i] are what the i-th function returned. When errs[i] isn’t nil, results[i] is the zero value, so a failed call’s result can’t be mistaken for a real one.

for i, err := range errs {
if err != nil {
log.Printf("shop %d: %v", i, err)
continue
}
use(prices[i])
}

All never stops early: one function failing doesn’t affect the others. To get a single error, join them: errors.Join(errs...) is nil when every function succeeded.

The first function runs on the goroutine that called All, and each of the others on a goroutine of its own, so they all run at the same time. A panic in the first one reaches your goroutine, where you can recover it.

examples/all/main.go
// Command all checks several URLs at once and prints each one's status, in the order given.
//
// go run ./examples/all https://go.dev https://pkg.go.dev https://example.invalid
package main
import (
"context"
"fmt"
"net/http"
"os"
"time"
"github.com/vardius/gollback/v2"
)
// check requests every URL concurrently and reports on each, whether or not the others failed.
func check(ctx context.Context, client *http.Client, urls ...string) []string {
checks := make([]gollback.AsyncFunc[string], len(urls))
for i, url := range urls {
checks[i] = func(ctx context.Context) (string, error) {
return status(ctx, client, url)
}
}
statuses, errs := gollback.All(ctx, checks...)
report := make([]string, len(urls))
for i, url := range urls {
if errs[i] != nil {
report[i] = fmt.Sprintf("%s: %v", url, errs[i])
continue
}
report[i] = fmt.Sprintf("%s: %s", url, statuses[i])
}
return report
}
func status(ctx context.Context, client *http.Client, url string) (string, error) {
req, err := http.NewRequestWithContext(ctx, http.MethodHead, url, nil)
if err != nil {
return "", err
}
resp, err := client.Do(req)
if err != nil {
return "", err
}
resp.Body.Close()
return resp.Status, nil
}
func main() {
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()
for _, line := range check(ctx, http.DefaultClient, os.Args[1:]...) {
fmt.Println(line)
}
}