Skip to content

Basic Examples

Fire-and-Forget

Spawn a background goroutine. Errors are logged automatically.

go
package main

import (
	"context"
	"fmt"
	"time"

	"github.com/foomo/gofuncy"
)

func main() {
	ctx := context.Background()

	gofuncy.Go(ctx, func(ctx context.Context) error {
		fmt.Println("running in the background")
		return nil
	})

	time.Sleep(100 * time.Millisecond)
}

Custom Error Handler

Override the default slog handler to handle errors yourself.

go
package main

import (
	"context"
	"errors"
	"fmt"
	"time"

	"github.com/foomo/gofuncy"
)

func main() {
	ctx := context.Background()

	gofuncy.Go(ctx, func(ctx context.Context) error {
		return errors.New("something went wrong")
	},
		gofuncy.WithName("custom-handler"),
		gofuncy.WithErrorHandler(func(ctx context.Context, err error) {
			fmt.Printf("[%s] error: %v\n", gofuncy.NameFromContext(ctx), err)
		}),
	)

	time.Sleep(100 * time.Millisecond)
	// Output: [custom-handler] error: something went wrong
}

Async with Deferred Result

Launch work now, collect the result when you need it. The wait function is safe to call from multiple goroutines.

go
package main

import (
	"context"
	"fmt"

	"github.com/foomo/gofuncy"
)

func main() {
	ctx := context.Background()

	var user string
	var orders []string

	// Launch two async calls
	waitUser := gofuncy.Wait(ctx, func(ctx context.Context) error {
		user = "Alice"
		return nil
	})

	waitOrders := gofuncy.Wait(ctx, func(ctx context.Context) error {
		orders = []string{"order-1", "order-2"}
		return nil
	})

	// Wait for both
	if err := waitUser(); err != nil {
		fmt.Println("user error:", err)
		return
	}
	if err := waitOrders(); err != nil {
		fmt.Println("orders error:", err)
		return
	}

	fmt.Printf("%s has %d orders\n", user, len(orders))
	// Output: Alice has 2 orders
}

Synchronous Execution with Do

Run a function through the full middleware chain without spawning a goroutine. Useful for inline calls that need retry, timeout, or circuit breaker.

go
package main

import (
	"context"
	"fmt"
	"time"

	"github.com/foomo/gofuncy"
)

func main() {
	ctx := context.Background()

	err := gofuncy.Do(ctx, func(ctx context.Context) error {
		// Simulate a flaky call
		return fmt.Errorf("connection refused")
	},
		gofuncy.WithRetry(3, gofuncy.RetryBackoff(gofuncy.BackoffConstant(100*time.Millisecond))),
		gofuncy.WithTimeout(500*time.Millisecond),
	)
	if err != nil {
		fmt.Println("failed after retries:", err)
	}
}

Basic Group

Run multiple functions concurrently and collect all errors.

go
package main

import (
	"context"
	"errors"
	"fmt"

	"github.com/foomo/gofuncy"
)

func main() {
	ctx := context.Background()

	g := gofuncy.NewGroup(ctx)

	g.Add(func(ctx context.Context) error {
		fmt.Println("task A")
		return nil
	})

	g.Add(func(ctx context.Context) error {
		fmt.Println("task B")
		return errors.New("task B failed")
	})

	g.Add(func(ctx context.Context) error {
		fmt.Println("task C")
		return nil
	})

	if err := g.Wait(); err != nil {
		fmt.Println("group error:", err)
	}
	// Output (order may vary):
	// task A
	// task B
	// task C
	// group error: task B failed
}

All Over a Slice

Iterate over items concurrently with a concurrency limit.

go
package main

import (
	"context"
	"fmt"
	"time"

	"github.com/foomo/gofuncy"
)

func main() {
	ctx := context.Background()

	items := []string{"alpha", "bravo", "charlie", "delta", "echo"}

	err := gofuncy.All(ctx, items, func(ctx context.Context, item string) error {
		fmt.Printf("processing %s\n", item)
		time.Sleep(50 * time.Millisecond) // simulate work
		return nil
	},
		gofuncy.WithLimit(2), // process 2 at a time
	)
	if err != nil {
		fmt.Println("errors:", err)
	}
}

Channel

Send and receive values through an observable channel with built-in metrics.

go
package main

import (
	"context"
	"fmt"

	"github.com/foomo/gofuncy/channel"
)

func main() {
	ctx := context.Background()

	ch := channel.New[int](channel.WithBuffer[int](5))

	// Send multiple values at once
	if err := ch.Send(ctx, 1, 2, 3, 4, 5); err != nil {
		fmt.Println("send error:", err)
		return
	}

	fmt.Printf("buffered: %d/%d\n", ch.Len(), ch.Cap())
	// Output: buffered: 5/5

	// Close and drain
	ch.Close()

	for v := range ch.Receive() {
		fmt.Println(v)
	}
}

Map with Result Collection

Transform items concurrently and collect results in order.

go
package main

import (
	"context"
	"fmt"

	"github.com/foomo/gofuncy"
)

func main() {
	ctx := context.Background()

	numbers := []int{1, 2, 3, 4, 5}

	squares, err := gofuncy.Map(ctx, numbers, func(ctx context.Context, n int) (int, error) {
		return n * n, nil
	})
	if err != nil {
		fmt.Println("error:", err)
		return
	}

	fmt.Println(squares) // [1 4 9 16 25]
}

Stoppable Goroutine with GoWithCancel

Spawn a goroutine and stop it later.

go
package main

import (
	"context"
	"fmt"
	"time"

	"github.com/foomo/gofuncy"
)

func main() {
	stop := gofuncy.GoWithCancel(context.Background(), func(ctx context.Context) error {
		for {
			select {
			case <-ctx.Done():
				fmt.Println("stopped")
				return nil
			case <-time.After(100 * time.Millisecond):
				fmt.Println("tick")
			}
		}
	})

	time.Sleep(350 * time.Millisecond)
	stop()
	time.Sleep(50 * time.Millisecond)
	// Output:
	// tick
	// tick
	// tick
	// stopped
}