Skip to content

Commit

Permalink
docs
Browse files Browse the repository at this point in the history
  • Loading branch information
JustinDFuller committed Sep 25, 2024
1 parent 39c9975 commit a41340e
Show file tree
Hide file tree
Showing 2 changed files with 50 additions and 0 deletions.
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
# first

[![Go Reference](https://pkg.go.dev/badge/github.com/justindfuller/first.svg)](https://pkg.go.dev/github.com/justindfuller/first)

Get the first result without an error.

## Usage
Expand Down
48 changes: 48 additions & 0 deletions first.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,48 @@ import (
multierror "github.com/justindfuller/go-multierror"
)

// ErrNothingToWaitOn occurs when you call First.Wait() before First.Do().
// If this is intentional, the error is safe to ignore.
// The error is provided as a sentinel error so you can check for it.
//
// Example:
//
// _, err := f.Wait()
// if errors.Is(err, first.ErrNothingToWaitOn) {
// // safe to continue?
// }
var ErrNothingToWaitOn = errors.New("First.Wait() called without anything to wait on")

// First returns the first non-error result.
// Think of it like a sync.WaitGroup, except it stops waiting after the first group completes.
// You can also think of it like an errgroup.Group, except in the error scenario, it waits for all errors before completing.
//
// First uses generics to provide type-safe responses.
//
// Example:
//
// var f first.First[*example]
// var f first.First[mySampleStruct]
// var f first.First[int64]
//
// You may use any type you need and it will be available to return from first.Do() and first.Wait().
//
// The zero value of First is ready to use, without further initialization.
// First should not be copied after first use.
// First is safe to use concurrently across multiple goroutines.
type First[T any] struct {
mut sync.Mutex
errors chan error
result chan T
count int
}

// Do executes the provided function in a goroutine.
// It works in tandem with Wait() to retrieve the first result.
//
// When returning, the error should only have a value if T does not.
// If the error is non-nil, T is ignored.
// Do does not inspect the value of T. So, if error is nil, T is returned.
func (f *First[T]) Do(fn func() (T, error)) {
f.mut.Lock()
defer f.mut.Unlock()
Expand All @@ -42,6 +75,21 @@ func (f *First[T]) Do(fn func() (T, error)) {
}()
}

// Wait for the first result or all errors.
//
// If you call Wait before Do, you will receive the ErrNothingToWaitOn error.
//
// Wait will block until a call to Do returns a nil error OR until all functions return a non-nil error.
// Neither Do nor Wait inspects the value of T, so any nil error value will result in Wait returning the value of T.
//
// Example:
//
// res, err := t.Wait()
// if err != nil {
// // all calls to Do() returned an error.
// }
//
// fmt.Println(res) // the first value returned by any call to Do().
func (f *First[T]) Wait() (T, error) {
f.mut.Lock()

Expand Down

0 comments on commit a41340e

Please sign in to comment.