Skip to content

A concurrency tool that gets the first value or all the errors.

License

Notifications You must be signed in to change notification settings

JustinDFuller/first

Repository files navigation

first

Go Reference Build Status Go Report Card Go Test Coverage

TL;DR

It is a concurrency tool that gets the first value or all the errors.

📖 Table of Contents 📖

What?

First is a synchronization tool. It gets the first result that does not return an error. Otherwise, it gets all the errors.

You might think of it as similar to sync.WaitGroup. Except, it either waits for the first result or all of the errors.

You might think of it as similar to an errgroup.Group. Except, it does not wait for all functions to return. It waits for the first function to return a value or it waits for all the functions to return an error.

When?

One example is retrieving from a fast and slow data store concurrently. You might do this if you have no issue putting the full load on both resources.

Basically, you might use it any time you want to concurrently perform multiple tasks, but only need to wait for one of them to complete without error.

Usage

Install

First, install the package.

go get github.com/justindfuller/first

Basic Example

Then, use it.

package main

type example struct{
    name string
}

func main() {
	var f first.First[*example]
	
	f.Do(func() (*example, error) {
		time.Sleep(10 * time.Millisecond)
	
		return &example{name: "one"}, nil
	})
	
	f.Do(func() (*example, error) {
		return &example{name: "two"}, nil
	})
	
	res, err := f.Wait()
	if err != nil {
		log.Fatalf("Error: %s", err)
	}
	
	log.Printf("Result: %v", res) // prints "two"
}

Context Example

It also supports using contexts.

package main

type example struct{
    name string
}

func main() {
	f, ctx := first.WithContext[*example](context.Background())
	
	f.Do(func() (*example, error) {
		select {
			case <-time.After(10 * time.Millisecond):		
				return &example{name: "one"}, nil
			case <-ctx.Done():
				log.Print("Skipped one")
				return nil, ctx.Err()
		}
	})
	
	f.Do(func() (*example, error) {
		select {
			case <-time.After(1 * time.Millisecond):		
				return &example{name: "two"}, nil
			case <-ctx.Done():
				log.Print("Skipped two")
				return nil, ctx.Err()
		}	
	})
	
	res, err := f.Wait()
	if err != nil {
		log.Fatalf("Error: %s", err)
	}
	
	log.Printf("Result: %v", res) // prints "two"
	// Also prints, "skipped one"

	log.Printf("Context: %s", ctx.Err())
	// Prints: "Context: context canceled"
}

Documentation

Please refer to the go documentation hosted on pkg.go.dev. You can see all available types and methods and runnable examples.