Skip to content

add Async::Promise #371

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 3 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 36 additions & 0 deletions lib/async/promise.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
# frozen_string_literal: true

# Released under the MIT License.
# Copyright, 2025, by Patrik Wenger.

require_relative "variable"

module Async

# Similar to a Variable, in that it allows a task to wait for a value to resolve, but also supports rejection given
# an error. The given error is raised as an exception in the waiting task.
class Promise < Variable
def initialize(...)
super

@error = nil
end

def reject(error = RuntimeError.new('promise rejected'), value = nil)
@error = error
resolve value
end

def rejected?
resolved? && !!@error
end

# @returns [Boolean] Whether the value has been resolved.
# @raises [Exception] The error with which this Variable was rejected
def wait
value = super
raise @error if @error
return value
end
end
end
2 changes: 1 addition & 1 deletion readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ Async is a composable asynchronous I/O framework for Ruby based on [io-event](ht
> [tus-ruby-server](https://github.com/janko/tus-ruby-server) – would really benefit from non-blocking I/O. It's really
> beautifully designed." *– [janko](https://github.com/janko)*

[![Development Status](https://github.com/socketry/async/workflows/Test/badge.svg)](https://github.com/socketry/async/actions?workflow=Test)
[![Development Status](https://github.com/socketry/async/workflows/Test/badge.svg)](https://github.com/socketry/async/actions?workflow=Test) [<img src="https://api.gitsponsors.com/api/badge/img?id=87380483" height="20">](https://api.gitsponsors.com/api/badge/link?p=U4gCxvzG7eUksiJSe0MSlN/IQp/rOEmM4wKYQwPrkiFn78vaBdtOdFQLsJu4pzyegaVzeek/1axVEWxd6fq1Ww==)

## Features

Expand Down
77 changes: 77 additions & 0 deletions test/async/promise.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
# frozen_string_literal: true

# Released under the MIT License.
# Copyright, 2025, by Patrik Wenger.

require "sus/fixtures/async"
require "async/promise"

PromiseContext = Sus::Shared("a promise") do
let(:promise) {Async::Promise.new}

it "can resolve the value" do
promise.resolve(value)
expect(promise).to be(:resolved?)
end

it "can wait for the value to be resolved" do
Async do
expect(promise.wait).to be == value
end

promise.resolve(value)
end

it "can wait for the value to be resolved using setter" do
Async do
expect(promise.wait).to be == value
end

promise.value = value
end

it "can't resolve it a 2nd time" do
promise.resolve(value)
expect do
promise.resolve(value)
end.to raise_exception(FrozenError)
end

it "can reject with an exception" do
promise.reject RuntimeError.new("boom")
expect(promise).to be(:resolved?)
expect(promise).to be(:rejected?)
end

it "can wait for a rejection with an error" do
Async do
expect do
promise.wait
end.to raise_exception(RuntimeError)
end

promise.reject(RuntimeError.new)
end
end

include Sus::Fixtures::Async::ReactorContext

describe true do
let(:value) {subject}
it_behaves_like PromiseContext
end

describe false do
let(:value) {subject}
it_behaves_like PromiseContext
end

describe nil do
let(:value) {subject}
it_behaves_like PromiseContext
end

describe Object do
let(:value) {subject.new}
it_behaves_like PromiseContext
end
8 changes: 8 additions & 0 deletions test/async/variable.rb
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,14 @@

variable.resolve(value)
end

it "can wait for the value to be resolved using setter" do
Async do
expect(variable.wait).to be == value
end

variable.value = value
end

it "can't resolve it a 2nd time" do
variable.resolve(value)
Expand Down
Loading