Ecosyste.ms: Awesome
An open API service indexing awesome lists of open source software.
https://github.com/spider-gazelle/promise
Type aware promises for crystal lang
https://github.com/spider-gazelle/promise
Last synced: about 1 month ago
JSON representation
Type aware promises for crystal lang
- Host: GitHub
- URL: https://github.com/spider-gazelle/promise
- Owner: spider-gazelle
- License: mit
- Created: 2018-08-01T08:50:07.000Z (over 6 years ago)
- Default Branch: master
- Last Pushed: 2024-03-03T01:45:35.000Z (9 months ago)
- Last Synced: 2024-08-01T17:36:23.998Z (4 months ago)
- Language: Crystal
- Homepage:
- Size: 62.5 KB
- Stars: 42
- Watchers: 3
- Forks: 3
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- License: LICENSE
Awesome Lists containing this project
- awesome-crystal - promise - A Promise implementation with type inference (Processes and Threads)
- awesome-crystal - promise - A Promise implementation with type inference (Processes and Threads)
README
# Crystal Lang Promises
[![CI](https://github.com/spider-gazelle/promise/actions/workflows/ci.yml/badge.svg)](https://github.com/spider-gazelle/promise/actions/workflows/ci.yml)
Provides a fully typed implementation of promises for crystal lang.
## A word of advice
It's good practice to only expose synchronous APIs in crystal lang.
If you are building a library that uses promises internally, make sure to call `#get` when returning on public interfaces.## Overview
A promise represents the eventual result of an operation.
You can use a promise to specify what to do when an operation eventually succeeds or fails.```crystal
require "promise"
# A promise defines the eventual type that will be returned
promise = Promise.new(String)# You can then perform some asynchronous action that will resolve the promise
channel.send {promise, arg1, arg2}# A callback can be used to access the result.
# All return values are nilable
promise.then do |result|
puts result
end# You can also handle Exceptions.
# All failure responses are of type Exception
promise.catch do |error|
puts error.message
end# If you don't care about the result but want to be notified of job completion
promise.finally do |error|
if error
job_failed
else
job_success
end
end# You can also pause execution and wait for the value
# This will raise an error if the promise was rejected
begin
result = promise.get
rescue error
puts error.message
end```
## Simple concurrency control
`Promise.defer` is similar to `spawn`, however you can wait for it to complete with a value or error.
The promise type is inferred from the return type of the defer block.```crystal
Promise.defer {
# your concurrent code here, in spawned fiber
}.then { |result|
# process returned value
}.catch { |error|
# handle errors
}```
## Promise.all
The `Promise.all(Enumerable)` method returns a single Promise that resolves when all of the promises in the argument(s) have resolved or when the argument contains no promises.
It rejects with the reason of the first promise that rejects.```crystal
# synchronous response
value1, value2 = Promise.all(
Promise.defer { function1 },
Promise.defer { function2 }
).get# using callbacks
Promise.all(
Promise.defer { function1 },
Promise.defer { function2 }
).then do |results|
results.each { }
end```
There are no restrictions placed on return types so function1 can return a String and function2 an Int32, for example.
## Promise.race
The `Promise.race(Enumerable)` method returns a promise that resolves or rejects as soon as one of the promises resolves or rejects, with the value or reason from that promise.
It will raise an error if no promises are provided.```crystal
Promise.race(
Promise.defer { sleep rand(0.0..1.0); "p1 wins" },
Promise.defer { sleep rand(0.0..1.0); "p2 wins" }
).get```
## Promise.map
Promise also support asynchronous maps over Enumerable types, yielding an array of the
resolved elements.```crystal
Promise.map([1, 2, 3]) do |x|
sleep x
x
end.sum # => 6
```## Multiple receivers
There might be multiple parties interested in the results of an operation.
Promises ensure all receivers receive the result when the operation is complete.```crystal
operation = Promise.defer {
# your concurrent code here, in spawned fiber
}operation.then do |result|
puts "log the result #{result}"
endoperation.then do |result|
email(result.contacts) if result.state == :not_good
end```
Of course new receivers will probably be added dynamically at runtime
## Promise chaining
Promises can be chained together and the return type of every `.then` block is used to build the next promise, so it is simple to transform values.
```crystal
Promise.defer {
# obtains the age of an imaginary person
get_age
}.catch { |error|
if error.message == "not born"
# We can recover the operation by returning a compatible value
0
else
raise error
end
}.then { |age|
# we can perform another async operation
Promise.defer {
print_birthday_card(age)
}
}.then {
log_success
}.catch { |error|
log_error(error)
}.finally {
print_next_card
}```