Skip to main content
Version: Next (unreleased)

Future

SubmitTyped runs a function that returns a value and gives you a typed *Future[T] for its result:

future := worker.SubmitTyped(pool, func(ctx context.Context) (Report, error) {
return buildReport(ctx, month)
})

// ...do other work...

report, err := future.Await(ctx)

The type parameter is inferred from the function, so there is no need to write SubmitTyped[Report].

Awaiting​

Await blocks until the result is ready or ctx ends. If ctx ends first, it returns the zero value and ctx.Err() — and the job keeps running, since the future is not tied to the caller's context. A later Await can still collect the result.

Done reports, without blocking, whether the result is ready:

if future.Done() {
report, err := future.Await(context.Background()) // returns immediately
}

Await can be called any number of times, from any number of goroutines, and always returns the same result.

Running several at once​

Futures are the simplest way to run a few independent calls in parallel and collect typed results:

userF := worker.SubmitTyped(pool, func(ctx context.Context) (*User, error) {
return users.Get(ctx, id)
})
ordersF := worker.SubmitTyped(pool, func(ctx context.Context) ([]Order, error) {
return orders.ForUser(ctx, id)
})

user, err := userF.Await(ctx)
if err != nil {
return err
}
orders, err := ordersF.Await(ctx)
if err != nil {
return err
}

If one failure should cancel the others, use an ErrorGroup instead.

How the result is decided​

OutcomeAwait returns
successthe value, nil
failure after all retriesthe value from the final attempt, its error
panicthe zero value, a *PanicError (matches ErrJobPanic)
pool closed before it could be queuedthe zero value, ErrPoolClosed
dropped by CloseWithTimeoutthe zero value, ErrPoolClosed

With retries enabled, earlier attempts' values are discarded; only the attempt that decides the job's outcome sets the result.

Submitting​

SubmitTyped submits like Submit: it waits for queue space if the queue is full, and the job runs with its own context, independent of the caller's. There is no non-blocking variant. Checking pool.QueueLength() first is only a hint, since other submitters can fill the queue in between; for real load shedding, use TrySubmit with a job that sends its result on a channel.