From c799d10ce920fbe63ba8f282c975b17b55c2c5eb Mon Sep 17 00:00:00 2001 From: dudaodong Date: Thu, 16 Mar 2023 15:34:28 +0800 Subject: [PATCH] feat: add promise All, Race, Any methods --- async/promise.go | 124 ++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 116 insertions(+), 8 deletions(-) diff --git a/async/promise.go b/async/promise.go index 4b963dd..8664ef5 100644 --- a/async/promise.go +++ b/async/promise.go @@ -1,7 +1,7 @@ // Copyright 2023 dudaodong@gmail.com. All rights reserved. // Use of this source code is governed by MIT license -// Package async contain some features to support async programming. eg, promise, asycn/await, eventbus. +// Package async contain some featurese to support async programming. eg, promise, asycn/await, eventbus. package async import ( @@ -11,6 +11,7 @@ import ( ) // Promise represents the eventual completion (or failure) of an asynchronous operation and its resulting value. +// ref : chebyrash/promise (https://github.com/chebyrash/promise) // see js promise: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise type Promise[T any] struct { runnable func(resolve func(T), reject func(error)) @@ -35,12 +36,6 @@ func New[T any](runnable func(resolve func(T), reject func(error))) *Promise[T] wg: &sync.WaitGroup{}, } - // p.wg.Add(1) - // go func() { - // defer p.handlePanic() - // runnable(p.resolve, p.reject) - // }() - defer p.run() return p @@ -113,7 +108,7 @@ func (p *Promise[T]) reject(err error) { } // Then allows chain calls to other promise methods. -func Then[T1, T2 any](promise Promise[T1], resolve1 func(value T1) T2) *Promise[T2] { +func Then[T1, T2 any](promise *Promise[T1], resolve1 func(value T1) T2) *Promise[T2] { return New(func(resolve2 func(T2), reject func(error)) { result, err := promise.Await() if err != nil { @@ -165,3 +160,116 @@ func (p *Promise[T]) Await() (T, error) { p.wg.Wait() return p.result, p.err } + +type tuple[T1, T2 any] struct { + _1 T1 + _2 T2 +} + +// All resolves when all of the promises have resolved, reject immediately upon any of the input promises rejecting. +func All[T any](promises []*Promise[T]) *Promise[[]T] { + if len(promises) == 0 { + return nil + } + + return New(func(resolve func([]T), reject func(error)) { + valsChan := make(chan tuple[T, int], len(promises)) + errsChan := make(chan error, 1) + + for idx, p := range promises { + idx := idx + _ = Then(p, func(data T) T { + valsChan <- tuple[T, int]{_1: data, _2: idx} + return data + }) + _ = Catch(p, func(err error) error { + errsChan <- err + return err + }) + } + + resolutions := make([]T, len(promises)) + for idx := 0; idx < len(promises); idx++ { + select { + case val := <-valsChan: + resolutions[val._2] = val._1 + case err := <-errsChan: + reject(err) + return + } + } + resolve(resolutions) + }) +} + +// Race will settle the first fullfiled promise among muti promises. +func Race[T any](promises []*Promise[T]) *Promise[T] { + if len(promises) == 0 { + return nil + } + + return New(func(resolve func(T), reject func(error)) { + valsChan := make(chan T, 1) + errsChan := make(chan error, 1) + + for _, p := range promises { + _ = Then(p, func(data T) T { + valsChan <- data + return data + }) + _ = Catch(p, func(err error) error { + errsChan <- err + return err + }) + } + + select { + case val := <-valsChan: + resolve(val) + case err := <-errsChan: + reject(err) + } + }) +} + +// Any resolves as soon as any of the input's Promises resolve, with the value of the resolved Promise. +// Any rejects if all of the given Promises are rejected with a combination of all errors. +func Any[T any](promises ...*Promise[T]) *Promise[T] { + if len(promises) == 0 { + return nil + } + + return New(func(resolve func(T), reject func(error)) { + valsChan := make(chan T, 1) + errsChan := make(chan tuple[error, int], len(promises)) + + for idx, p := range promises { + idx := idx + _ = Then(p, func(data T) T { + valsChan <- data + return data + }) + _ = Catch(p, func(err error) error { + errsChan <- tuple[error, int]{_1: err, _2: idx} + return err + }) + } + + errs := make([]error, len(promises)) + for idx := 0; idx < len(promises); idx++ { + select { + case val := <-valsChan: + resolve(val) + return + case err := <-errsChan: + errs[err._2] = err._1 + } + } + + errCombo := errs[0] + // for _, err := range errs[1:] { + // errCombo = errors. + // } + reject(errCombo) + }) +}