Per-invocation timeout. Each retry attempt gets a fresh deadline.
WithRetry(n, opts...)
Automatic retry with configurable backoff.
WithCircuitBreaker(cb)
Fail fast on broken dependencies. Stateful — share across calls.
WithFallback(fn, opts...)
Called when the operation fails. Return nil to suppress the error.
Telemetry
Option
Default
Description
WithoutTracing()
on
Disable span creation.
WithDetachedTrace()
varies
Root span linked to parent instead of child span. Default for Go/Start/StartWithReady/StartWithStop/GoWithCancel.
WithChildTrace()
varies
Force child span. Default for Do/Wait/NewGroup.
WithoutStartedCounter()
on
Disable started counter.
WithoutErrorCounter()
on
Disable error counter.
WithoutActiveUpDownCounter()
on
Disable active counter.
WithDurationHistogram()
off
Enable duration histogram.
WithMeterProvider(mp)
global
Custom OTel meter provider.
WithTracerProvider(tp)
global
Custom OTel tracer provider.
Concurrency
Option
Description
WithLimiter(sem)
Shared *semaphore.Weighted for cross-callsite concurrency control.
Middleware
Option
Description
WithMiddleware(m...)
Append custom middleware. Applied after resilience, before telemetry.
WithLogger(l)
Custom *slog.Logger for error reporting.
WithStallThreshold(d)
Log a warning if the goroutine runs longer than d.
WithStallHandler(h)
Custom callback for stall detection.
Error Handling
Option
Description
WithCallerSkip(n)
Adjust stack depth for span caller attributes.
Behavior
The full middleware chain is built: panic recovery, resilience (timeout, retry, circuit breaker, fallback), user middlewares, metrics, tracing, stall detection.
If a WithLimiter semaphore is set, it is acquired before spawning.
A goroutine is spawned to execute the function.
A wait function is returned immediately.
Calling the wait function blocks until the goroutine completes and returns its error.
The wait function is safe to call multiple times and from multiple goroutines — it always returns the same result.
WaitWithStop
Like Wait, but the goroutine receives a StopFunc it can call to cancel its own context. Returns a wait function for deferred result collection.
wait:=gofuncy.WaitWithStop(ctx,func(ctxcontext.Context,stopStopFunc)error{for{select{case<-ctx.Done():returnnilcasemsg:=<-incoming:ifmsg=="done"{stop()// self-cancel}process(msg)}}})// Later, collect the result:iferr:=wait();err!=nil{log.Println("error:",err)}
WaitWithReady
Like Wait, but the goroutine receives a ReadyFunc it can call to signal readiness. The caller blocks until ready() is called or the function returns, then receives a wait function for deferred result collection.
wait:=gofuncy.WaitWithReady(ctx,func(ctxcontext.Context,readygofuncy.ReadyFunc)error{// Perform initializationiferr:=initResources(ctx);err!=nil{returnerr}ready()// signal that initialization is complete// Continue running until context is cancelled<-ctx.Done()returnnil})// At this point, initialization is guaranteed to be complete (or failed).// Later, collect the result:iferr:=wait();err!=nil{log.Println("error:",err)}
Do vs Wait vs WaitWithStop vs WaitWithReady vs Go
Do
Wait
WaitWithStop
WaitWithReady
Go
Execution
Synchronous
Async — returns wait function
Async — returns wait function
Async — blocks until ready, returns wait function
Async — fire-and-forget
Error handling
Returns error
Wait function returns error
Wait function returns error
Wait function returns error
ErrorHandler callback
Stop control
—
—
Goroutine itself
—
—
Ready signal
—
—
—
Goroutine signals readiness
—
Use case
Inline call with resilience
Launch now, collect result later
Self-cancelling goroutine with result
Goroutine with initialization gate
Background work
Example
// Launch two async callswaitUser:=gofuncy.Wait(ctx,func(ctxcontext.Context)error{user,err=api.GetUser(ctx,userID)returnerr},gofuncy.WithRetry(3))waitOrders:=gofuncy.Wait(ctx,func(ctxcontext.Context)error{orders,err=api.GetOrders(ctx,userID)returnerr},gofuncy.WithRetry(3))// Wait for bothiferr:=waitUser();err!=nil{returnerr}iferr:=waitOrders();err!=nil{returnerr}