Go

Implement a Future/Promise type in Go with Get(ctx) and error propagation using generics.

Question 523MediumGo 1.22 to 1.25

The future is a struct holding the result plus a done channel that is closed exactly once when the result is ready. The Go memory model guarantees that a close happens-before any receive that returns because of it. So once <-f.done returns, reading val and err is race-free with no mutex. Any number of callers can call Get, and they all see the same result.

type Future[T any] struct {
	done chan struct{}
	val  T
	err  error
}

func Async[T any](ctx context.Context, fn func(context.Context) (T, error)) *Future[T] {
	f := &Future[T]{done: make(chan struct{})}
	go func() {
		defer close(f.done) // runs last (defers are LIFO)
		defer func() {
			if r := recover(); r != nil {
				f.err = fmt.Errorf("future panicked: %v", r)
			}
		}()
		f.val, f.err = fn(ctx)
	}()
	return f
}

func (f *Future[T]) Get(ctx context.Context) (T, error) {
	select {
	case <-f.done:
		return f.val, f.err
	case <-ctx.Done():
		var zero T
		return zero, ctx.Err()
	}
}

// Then must be a function, not a method: Go methods cannot declare
// their own type parameters.
func Then[T, U any](ctx context.Context, f *Future[T], fn func(T) (U, error)) *Future[U] {
	return Async(ctx, func(ctx context.Context) (U, error) {
		v, err := f.Get(ctx)
		if err != nil {
			var zero U
			return zero, err
		}
		return fn(v)
	})
}

Gotchas:

  • Get(ctx) timing out does not stop the computation. Only the ctx passed to Async can do that, and only if fn honours it.
  • Recovering the panic inside the goroutine matters. An unrecovered panic in any goroutine kills the whole process.
  • The defer order is deliberate. recover sets err before close publishes the result.
  • func (f *Future[T]) Then[U any] does not compile: methods can't declare their own type parameters.

In real code, errgroup.Group or a plain buffered chan result is often enough. Interviewers want to see that you know the happens-before guarantee, not a JavaScript port.

More on Classic Concurrency Coding Problems

All 16 Classic Concurrency Coding Problems questions