PAS is a Go package that provides a simple and efficient way to handle asynchronous computations using Promises. By simply wrapping your functions in pas.Async[T](yourFunction, args...), you can instantly make them parallel, without any extra code.
Promises are resolved automatically when they are used as arguments to other PAS-wrapped functions. Promises that are nested arbitrarily deep inside slices, maps, or pointers are also resolved automatically.
// 🔄 Sequential
array := make([]int, 100)
for i := range array {
if i <= 50 {
array[i] = Compute(i, 0) // Some intensive computation
} else {
array[i] = Compute(i, array[i-50]) // Complex inter-dependency
}
}
sum := Sum(array)
fmt.Println(sum)⬇️ ⬇️ ⬇️
// ⚡️ Parallel
array := pas.MakeSlice[int](10) // or: make([]*pas.Promise[int], 10)
for i := range array {
if i <= 5 {
// No need to change implementation of Compute
array[i] = pas.Async[int](Compute, i, 0)
} else {
// Dependencies are automatically handled
array[i] = pas.Async[int](Compute, i, array[i-5])
}
}
// Promised values inside slices/maps/pointers are automatically resolved
sum := pas.Sync[int](Sum, array)
fmt.Println(sum)- Promises: Represent future values, allowing you to handle results of asynchronous operations.
- Async: Execute functions in parallel with automatic handling of dependent (even nested) Promises.
- Sync: Execute functions synchronously with automatic handling of dependent (even nested) Promises.
To install or upgrade the latest version of PAS package, use go get:
go get -u github.com/AnJunHao/pasCreate a new Promise using the New function. The Promise is immediately ready. You can initialize it with an optional value.
import "github.com/AnJunHao/pas"
// Create a promise with an initial value
p := pas.New(42)
// Create a promise with its initial value as the default value of type T
p := pas.New[int]()Use the Async function to execute a function in parallel. It takes a function and its arguments, which can include other Promises or nested Promises. Dependent Promises or nested Promises are resolved automatically.
import "github.com/AnJunHao/pas"
func Compute(x, y int) int {
// Some intensive computation
return x + y
}
p := pas.Async[int](Compute, 5, 10)
result := p.Get() // Blocks until the computation is doneUse the Sync function to execute a function synchronously. It automatically waits for any Promises or nested Promises passed as arguments to be resolved.
import "github.com/AnJunHao/pas"
func Multiply(x, y int) int {
return x * y
}
p1 := pas.Async[int](Compute, 5, 10)
result := pas.Sync[int](Multiply, p1, 3)Here's a complete example demonstrating parallel sum computation:
// main.go
package main
import (
"fmt"
"time"
"github.com/AnJunHao/pas"
)
func SumWithinRange(start int, end int) int {
sum := 0
for i := start; i <= end; i++ {
sum += i
}
return sum
}
func Add(a, b int) int {
return a + b
}
func main() {
n := 1000000000
numWorkers := 20
startTime := time.Now()
// Parallel Sum
parSum := pas.New(0)
for i := 0; i < numWorkers; i++ {
start := i*n/numWorkers + 1
end := (i + 1) * n / numWorkers
s := pas.Async[int](SumWithinRange, start, end)
parSum = pas.Async[int](Add, parSum, s)
}
// Get the result
fmt.Println(parSum.Get())
duration := time.Since(startTime)
fmt.Printf("Parallel Sum took: %v\n", duration)
// Sequential Sum
startTime = time.Now()
seqSum := SumWithinRange(0, n)
duration = time.Since(startTime)
fmt.Printf("Sequential Sum took: %v\n", duration)
if seqSum == parSum.Get() {
fmt.Println("Success: Sequential and Parallel results match.")
} else {
fmt.Println("Error: Results do not match.")
}
}Represents a parallel variable holding a value of type T.
type Promise[T any] struct {
value T
ready chan struct{}
once sync.Once
}Returns the computed value, blocking until it is ready.
func (p *Promise[T]) Get() TCreates a pointer to a new Promise with an optional initial value. The Promise is immediately ready.
func New[T any](value ...T) *Promise[T]Starts a parallel computation by invoking function f with the provided arguments. If any argument is a Promise, it waits for it to be ready before executing f.
func Async[T any](f interface{}, args ...interface{}) *Promise[T]Parameters:
f: The function to execute asynchronously. It must have exactly one return value of typeT.args: Arguments to pass to the function. Can include Promises.
Returns:
*Promise[T]: A Promise representing the future result of the computation.
Executes function f synchronously with the provided arguments. If any argument is a Promise, it waits for it to be ready before executing f.
func Sync[T any](f interface{}, args ...interface{}) TParameters:
f: The function to execute synchronously. It must have exactly one return value of typeT.args: Arguments to pass to the function. Can include Promises.
Returns:
T: The result of the function execution.
Creates a slice of *Promise[T] with the specified length and capacity. The Promises are immediately ready.
func MakeSlice[T any](length int, capacity ...int) []*Promise[T]Parameters:
length: The number of elements in the slice.capacity(optional): The capacity of the slice.
Returns:
[]*Promise[T]: A slice of Promises.
Usage:
promises := pas.MakeSlice[int](5)Creates a map with keys of type K and values of type *Promise[V].
func MakeMap[K comparable, V any](size ...int) map[K]*Promise[V]Parameters:
size(optional): Initial size of the map.
Returns:
map[K]*Promise[V]: A map with Promises as values.
Usage:
promiseMap := pas.MakeMap[string, int](10)AsyncandSynconly work with functions that return exactly one value.AsyncandSyncdo not work with methods (functions with a receiver).AsyncandSyncdo not work with variadic functions (functions with a variable number of arguments).- No pooling mechanism is implemented (yet). Each call to
Asynccreates a new goroutine. - We intentionally unexported methods like
Promise.resolveandnewPendingto simplify API surface.
AsyncandSynccalls an internal functionexecuteFunctionthat handles the details of resolving arguments and calling the function.executeFunctionuses reflection to inspect the type and value of each argument, and callsresolveValueto resolve Promises and nested Promises recursively.resolveValuerecursively resolves Promises within the input based on the expected type. It handles Promises, pointers, slices, arrays, maps, and nested combinations thereof. Non-Promise arguments are returned as-is.- Example inputs and expected outputs:
*Promise[int]->int[]*Promise[int]->[]int[]map[string]*Promise[[]map[string]int]->[]map[string][]map[string]int
- Example inputs and expected outputs:
This project is licensed under the MIT License. See the LICENSE file for details.