hunch

package module
v1.1.3 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: May 24, 2022 License: Apache-2.0 Imports: 4 Imported by: 6

README

Housekeeper

GitHub tag (latest SemVer) Build status codecov Go Report Card GitHub GoDoc

Hunch

Hunch provides functions like: All, First, Retry, Waterfall etc., that makes asynchronous flow control more intuitive.

About Hunch

Go have several concurrency patterns, here're some articles:

But nowadays, using the context package is the most powerful pattern.

So base on context, Hunch provides functions that can help you deal with complex asynchronous logics with ease.

Usage

Installation
go get
$ go get -u -v github.com/aaronjan/hunch
import "github.com/aaronjan/hunch"
$ go mod tidy
Types
type Executable func(context.Context) (interface{}, error)

type ExecutableInSequence func(context.Context, interface{}) (interface{}, error)
API
All
func All(parentCtx context.Context, execs ...Executable) ([]interface{}, error) 

All returns all the outputs from all Executables, order guaranteed.

Examples
ctx := context.Background()
r, err := hunch.All(
    ctx,
    func(ctx context.Context) (interface{}, error) {
        time.Sleep(300 * time.Millisecond)
        return 1, nil
    },
    func(ctx context.Context) (interface{}, error) {
        time.Sleep(200 * time.Millisecond)
        return 2, nil
    },
    func(ctx context.Context) (interface{}, error) {
        time.Sleep(100 * time.Millisecond)
        return 3, nil
    },
)

fmt.Println(r, err)
// Output:
// [1 2 3] <nil>
Take
func Take(parentCtx context.Context, num int, execs ...Executable) ([]interface{}, error)

Take returns the first num values outputted by the Executables.

Examples
ctx := context.Background()
r, err := hunch.Take(
    ctx,
    // Only need the first 2 values.
    2,
    func(ctx context.Context) (interface{}, error) {
        time.Sleep(300 * time.Millisecond)
        return 1, nil
    },
    func(ctx context.Context) (interface{}, error) {
        time.Sleep(200 * time.Millisecond)
        return 2, nil
    },
    func(ctx context.Context) (interface{}, error) {
        time.Sleep(100 * time.Millisecond)
        return 3, nil
    },
)

fmt.Println(r, err)
// Output:
// [3 2] <nil>
Last
func Last(parentCtx context.Context, num int, execs ...Executable) ([]interface{}, error)

Last returns the last num values outputted by the Executables.

Examples
ctx := context.Background()
r, err := hunch.Last(
    ctx,
    // Only need the last 2 values.
    2,
    func(ctx context.Context) (interface{}, error) {
        time.Sleep(300 * time.Millisecond)
        return 1, nil
    },
    func(ctx context.Context) (interface{}, error) {
        time.Sleep(200 * time.Millisecond)
        return 2, nil
    },
    func(ctx context.Context) (interface{}, error) {
        time.Sleep(100 * time.Millisecond)
        return 3, nil
    },
)

fmt.Println(r, err)
// Output:
// [2 1] <nil>
Waterfall
func Waterfall(parentCtx context.Context, execs ...ExecutableInSequence) (interface{}, error)

Waterfall runs ExecutableInSequences one by one, passing previous result to next Executable as input. When an error occurred, it stop the process then returns the error. When the parent Context canceled, it returns the Err() of it immediately.

Examples
ctx := context.Background()
r, err := hunch.Waterfall(
    ctx,
    func(ctx context.Context, n interface{}) (interface{}, error) {
        return 1, nil
    },
    func(ctx context.Context, n interface{}) (interface{}, error) {
        return n.(int) + 1, nil
    },
    func(ctx context.Context, n interface{}) (interface{}, error) {
        return n.(int) + 1, nil
    },
)

fmt.Println(r, err)
// Output:
// 3 <nil>
Retry
func Retry(parentCtx context.Context, retries int, fn Executable) (interface{}, error)

Retry attempts to get a value from an Executable instead of an Error. It will keeps re-running the Executable when failed no more than retries times. Also, when the parent Context canceled, it returns the Err() of it immediately.

Examples
count := 0
getStuffFromAPI := func() (int, error) {
    if count == 5 {
        return 1, nil
    }
    count++

    return 0, fmt.Errorf("timeout")
}

ctx := context.Background()
r, err := hunch.Retry(
    ctx,
    10,
    func(ctx context.Context) (interface{}, error) {
        rs, err := getStuffFromAPI()

        return rs, err
    },
)

fmt.Println(r, err, count)
// Output:
// 1 <nil> 5

Credits

Heavily inspired by Async and ReactiveX.

Licence

Apache 2.0

Documentation

Overview

Package hunch provides functions like: `All`, `First`, `Retry`, `Waterfall` etc., that makes asynchronous flow control more intuitive.

Index

Examples

Constants

This section is empty.

Variables

This section is empty.

Functions

func All

func All(parentCtx context.Context, execs ...Executable) ([]interface{}, error)

All returns all the outputs from all Executables, order guaranteed.

Example
package main

import (
	"context"
	"fmt"
	"time"

	"github.com/aaronjan/hunch"
)

func main() {
	ctx := context.Background()
	r, err := hunch.All(
		ctx,
		func(ctx context.Context) (interface{}, error) {
			time.Sleep(300 * time.Millisecond)
			return 1, nil
		},
		func(ctx context.Context) (interface{}, error) {
			time.Sleep(200 * time.Millisecond)
			return 2, nil
		},
		func(ctx context.Context) (interface{}, error) {
			time.Sleep(100 * time.Millisecond)
			return 3, nil
		},
	)

	fmt.Println(r, err)
}
Output:

[1 2 3] <nil>

func Last

func Last(parentCtx context.Context, num int, execs ...Executable) ([]interface{}, error)

Last returns the last `num` values outputted by the Executables.

Example
package main

import (
	"context"
	"fmt"
	"time"

	"github.com/aaronjan/hunch"
)

func main() {
	ctx := context.Background()
	r, err := hunch.Last(
		ctx,
		// Only need the last 2 values.
		2,
		func(ctx context.Context) (interface{}, error) {
			time.Sleep(300 * time.Millisecond)
			return 1, nil
		},
		func(ctx context.Context) (interface{}, error) {
			time.Sleep(200 * time.Millisecond)
			return 2, nil
		},
		func(ctx context.Context) (interface{}, error) {
			time.Sleep(100 * time.Millisecond)
			return 3, nil
		},
	)

	fmt.Println(r, err)
}
Output:

[2 1] <nil>

func Retry

func Retry(parentCtx context.Context, retries int, fn Executable) (interface{}, error)

Retry attempts to get a value from an Executable instead of an Error. It will keeps re-running the Executable when failed no more than `retries` times. Also, when the parent Context canceled, it returns the `Err()` of it immediately.

Example
package main

import (
	"context"
	"fmt"

	"github.com/aaronjan/hunch"
)

func main() {
	count := 0
	getStuffFromAPI := func() (int, error) {
		if count == 5 {
			return 1, nil
		}
		count++

		return 0, fmt.Errorf("timeout")
	}

	ctx := context.Background()
	r, err := hunch.Retry(
		ctx,
		10,
		func(ctx context.Context) (interface{}, error) {
			rs, err := getStuffFromAPI()

			return rs, err
		},
	)

	fmt.Println(r, err, count)
}
Output:

1 <nil> 5

func Take

func Take(parentCtx context.Context, num int, execs ...Executable) ([]interface{}, error)

Take returns the first `num` values outputted by the Executables.

Example
package main

import (
	"context"
	"fmt"
	"time"

	"github.com/aaronjan/hunch"
)

func main() {
	ctx := context.Background()
	r, err := hunch.Take(
		ctx,
		// Only need the first 2 values.
		2,
		func(ctx context.Context) (interface{}, error) {
			time.Sleep(300 * time.Millisecond)
			return 1, nil
		},
		func(ctx context.Context) (interface{}, error) {
			time.Sleep(200 * time.Millisecond)
			return 2, nil
		},
		func(ctx context.Context) (interface{}, error) {
			time.Sleep(100 * time.Millisecond)
			return 3, nil
		},
	)

	fmt.Println(r, err)
}
Output:

[3 2] <nil>

func Waterfall

func Waterfall(parentCtx context.Context, execs ...ExecutableInSequence) (interface{}, error)

Waterfall runs `ExecutableInSequence`s one by one, passing previous result to next Executable as input. When an error occurred, it stop the process then returns the error. When the parent Context canceled, it returns the `Err()` of it immediately.

Example
package main

import (
	"context"
	"fmt"

	"github.com/aaronjan/hunch"
)

func main() {
	ctx := context.Background()
	r, err := hunch.Waterfall(
		ctx,
		func(ctx context.Context, n interface{}) (interface{}, error) {
			return 1, nil
		},
		func(ctx context.Context, n interface{}) (interface{}, error) {
			return n.(int) + 1, nil
		},
		func(ctx context.Context, n interface{}) (interface{}, error) {
			return n.(int) + 1, nil
		},
	)

	fmt.Println(r, err)
}
Output:

3 <nil>

Types

type Executable

type Executable func(context.Context) (interface{}, error)

Executable represents a singular logic block. It can be used with several functions.

type ExecutableInSequence added in v1.1.0

type ExecutableInSequence func(context.Context, interface{}) (interface{}, error)

ExecutableInSequence represents one of a sequence of logic blocks.

type IndexedExecutableOutput

type IndexedExecutableOutput struct {
	Value IndexedValue
	Err   error
}

IndexedExecutableOutput stores both output and error values from a Excetable.

type IndexedValue

type IndexedValue struct {
	Index int
	Value interface{}
}

IndexedValue stores the output of Executables, along with the index of the source Executable for ordering.

type MaxRetriesExceededError

type MaxRetriesExceededError struct {
	// contains filtered or unexported fields
}

MaxRetriesExceededError stores how many times did an Execution run before exceeding the limit. The retries field holds the value.

func (MaxRetriesExceededError) Error

func (err MaxRetriesExceededError) Error() string

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL