{"id":26601653,"url":"https://github.com/aaronjan/hunch","last_synced_at":"2025-08-26T12:10:22.773Z","repository":{"id":70445632,"uuid":"190398773","full_name":"AaronJan/Hunch","owner":"AaronJan","description":"Hunch provides functions like: All, First, Retry, Waterfall etc., that makes asynchronous flow control more intuitive.","archived":false,"fork":false,"pushed_at":"2022-06-26T13:00:20.000Z","size":40,"stargazers_count":105,"open_issues_count":2,"forks_count":12,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-05-31T23:56:38.714Z","etag":null,"topics":["async","asynchronous","channel","concurrency","concurrent","context","go","golang","reactivex","util"],"latest_commit_sha":null,"homepage":"","language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/AaronJan.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null}},"created_at":"2019-06-05T13:21:04.000Z","updated_at":"2025-01-15T02:40:01.000Z","dependencies_parsed_at":"2023-03-06T01:45:12.738Z","dependency_job_id":null,"html_url":"https://github.com/AaronJan/Hunch","commit_stats":null,"previous_names":[],"tags_count":8,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/AaronJan%2FHunch","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/AaronJan%2FHunch/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/AaronJan%2FHunch/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/AaronJan%2FHunch/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/AaronJan","download_url":"https://codeload.github.com/AaronJan/Hunch/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/AaronJan%2FHunch/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":258559895,"owners_count":22720455,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["async","asynchronous","channel","concurrency","concurrent","context","go","golang","reactivex","util"],"created_at":"2025-03-23T18:41:12.515Z","updated_at":"2025-06-13T19:05:14.347Z","avatar_url":"https://github.com/AaronJan.png","language":"Go","readme":"\u003ca href=\"https://github.com/AaronJan/Hunch\"\u003e\n\t\u003cimg width=\"160\" src=\"https://user-images.githubusercontent.com/4630940/59684078-ea9d8c80-920b-11e9-9c99-85051dcb8a04.jpg\" alt=\"Housekeeper\" title=\"Hunch\" align=\"right\"/\u003e\n\u003c/a\u003e\n\n![GitHub tag (latest SemVer)](https://img.shields.io/github/tag/aaronjan/hunch.svg)\n![Build status](https://github.com/aaronjan/hunch/workflows/Go/badge.svg?branch=master)\n[![codecov](https://codecov.io/gh/AaronJan/Hunch/branch/master/graph/badge.svg)](https://codecov.io/gh/AaronJan/Hunch)\n[![Go Report Card](https://goreportcard.com/badge/github.com/aaronjan/hunch)](https://goreportcard.com/report/github.com/aaronjan/hunch)\n![GitHub](https://img.shields.io/github/license/aaronjan/hunch.svg)\n[![GoDoc](https://godoc.org/github.com/aaronjan/hunch?status.svg)](https://godoc.org/github.com/aaronjan/hunch)\n\n# Hunch\n\nHunch provides functions like: `All`, `First`, `Retry`, `Waterfall` etc., that makes asynchronous flow control more intuitive.\n\n## About Hunch\n\nGo have several concurrency patterns, here're some articles:\n\n* https://blog.golang.org/pipelines\n* https://blog.golang.org/context\n* https://blog.golang.org/go-concurrency-patterns-timing-out-and\n* https://blog.golang.org/advanced-go-concurrency-patterns\n\nBut nowadays, using the `context` package is the most powerful pattern.\n\nSo base on `context`, Hunch provides functions that can help you deal with complex asynchronous logics with ease.\n\n## Usage\n\n### Installation\n\n#### `go get`\n\n```shell\n$ go get -u -v github.com/aaronjan/hunch\n```\n\n#### `go mod` (Recommended)\n\n```go\nimport \"github.com/aaronjan/hunch\"\n```\n\n```shell\n$ go mod tidy\n```\n\n### Types\n\n```go\ntype Executable func(context.Context) (interface{}, error)\n\ntype ExecutableInSequence func(context.Context, interface{}) (interface{}, error)\n```\n\n### API\n\n#### All\n\n```go\nfunc All(parentCtx context.Context, execs ...Executable) ([]interface{}, error) \n```\n\nAll returns all the outputs from all Executables, order guaranteed.\n\n##### Examples\n\n```go\nctx := context.Background()\nr, err := hunch.All(\n    ctx,\n    func(ctx context.Context) (interface{}, error) {\n        time.Sleep(300 * time.Millisecond)\n        return 1, nil\n    },\n    func(ctx context.Context) (interface{}, error) {\n        time.Sleep(200 * time.Millisecond)\n        return 2, nil\n    },\n    func(ctx context.Context) (interface{}, error) {\n        time.Sleep(100 * time.Millisecond)\n        return 3, nil\n    },\n)\n\nfmt.Println(r, err)\n// Output:\n// [1 2 3] \u003cnil\u003e\n```\n\n#### Take\n\n```go\nfunc Take(parentCtx context.Context, num int, execs ...Executable) ([]interface{}, error)\n```\n\nTake returns the first `num` values outputted by the Executables.\n\n##### Examples\n\n```go\nctx := context.Background()\nr, err := hunch.Take(\n    ctx,\n    // Only need the first 2 values.\n    2,\n    func(ctx context.Context) (interface{}, error) {\n        time.Sleep(300 * time.Millisecond)\n        return 1, nil\n    },\n    func(ctx context.Context) (interface{}, error) {\n        time.Sleep(200 * time.Millisecond)\n        return 2, nil\n    },\n    func(ctx context.Context) (interface{}, error) {\n        time.Sleep(100 * time.Millisecond)\n        return 3, nil\n    },\n)\n\nfmt.Println(r, err)\n// Output:\n// [3 2] \u003cnil\u003e\n```\n\n#### Last\n\n```go\nfunc Last(parentCtx context.Context, num int, execs ...Executable) ([]interface{}, error)\n```\n\nLast returns the last `num` values outputted by the Executables.\n\n##### Examples\n\n```go\nctx := context.Background()\nr, err := hunch.Last(\n    ctx,\n    // Only need the last 2 values.\n    2,\n    func(ctx context.Context) (interface{}, error) {\n        time.Sleep(300 * time.Millisecond)\n        return 1, nil\n    },\n    func(ctx context.Context) (interface{}, error) {\n        time.Sleep(200 * time.Millisecond)\n        return 2, nil\n    },\n    func(ctx context.Context) (interface{}, error) {\n        time.Sleep(100 * time.Millisecond)\n        return 3, nil\n    },\n)\n\nfmt.Println(r, err)\n// Output:\n// [2 1] \u003cnil\u003e\n```\n\n#### Waterfall\n\n```go\nfunc Waterfall(parentCtx context.Context, execs ...ExecutableInSequence) (interface{}, error)\n```\n\nWaterfall 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.\n\n##### Examples\n\n```go\nctx := context.Background()\nr, err := hunch.Waterfall(\n    ctx,\n    func(ctx context.Context, n interface{}) (interface{}, error) {\n        return 1, nil\n    },\n    func(ctx context.Context, n interface{}) (interface{}, error) {\n        return n.(int) + 1, nil\n    },\n    func(ctx context.Context, n interface{}) (interface{}, error) {\n        return n.(int) + 1, nil\n    },\n)\n\nfmt.Println(r, err)\n// Output:\n// 3 \u003cnil\u003e\n```\n\n#### Retry\n\n```go\nfunc Retry(parentCtx context.Context, retries int, fn Executable) (interface{}, error)\n```\n\nRetry 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.\n\n##### Examples\n\n```go\ncount := 0\ngetStuffFromAPI := func() (int, error) {\n    if count == 5 {\n        return 1, nil\n    }\n    count++\n\n    return 0, fmt.Errorf(\"timeout\")\n}\n\nctx := context.Background()\nr, err := hunch.Retry(\n    ctx,\n    10,\n    func(ctx context.Context) (interface{}, error) {\n        rs, err := getStuffFromAPI()\n\n        return rs, err\n    },\n)\n\nfmt.Println(r, err, count)\n// Output:\n// 1 \u003cnil\u003e 5\n```\n\n## Credits\n\nHeavily inspired by [Async](https://github.com/caolan/async/) and [ReactiveX](http://reactivex.io/).\n\n## Licence\n\n[Apache 2.0](https://www.apache.org/licenses/LICENSE-2.0)\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Faaronjan%2Fhunch","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Faaronjan%2Fhunch","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Faaronjan%2Fhunch/lists"}