66
77require_relative "error"
88require_relative "deadline"
9+ require_relative "cancel"
910
1011module Async
1112 # A promise represents a value that will be available in the future.
@@ -77,42 +78,6 @@ def value
7778 @mutex . synchronize { @resolved ? @value : nil }
7879 end
7980
80- # Wait for the promise to be resolved and return the value.
81- # If already resolved, returns immediately. If rejected, raises the stored exception.
82- #
83- # @parameter timeout [Numeric | Nil] Maximum time to wait. If nil, waits indefinitely. If 0, raises immediately if not resolved.
84- # @returns [Object] The resolved value.
85- # @raises [Exception] The rejected or cancelled exception.
86- # @raises [Async::TimeoutError] If timeout expires before the promise is resolved.
87- def wait ( timeout : nil )
88- @mutex . synchronize do
89- # Increment waiting count:
90- @waiting += 1
91-
92- begin
93- # Wait for resolution if not already resolved:
94- unless @resolved
95- if timeout . nil?
96- wait_indefinitely
97- else
98- wait_with_timeout ( timeout )
99- end
100- end
101-
102- # Return value or raise exception based on resolution type:
103- if @resolved == :completed
104- return @value
105- else
106- # Both :failed and :cancelled store exceptions in @value
107- raise @value
108- end
109- ensure
110- # Decrement waiting count when done:
111- @waiting -= 1
112- end
113- end
114- end
115-
11681 # Wait indefinitely for the promise to be resolved.
11782 private def wait_indefinitely
11883 until @resolved
@@ -122,14 +87,14 @@ def wait(timeout: nil)
12287
12388 # Wait for the promise to be resolved, respecting the deadline timeout.
12489 # @parameter timeout [Numeric] The timeout duration.
125- # @raises [Async::TimeoutError] If the timeout expires before resolution .
90+ # @returns [Boolean] True if resolved, false if timeout expires .
12691 private def wait_with_timeout ( timeout )
12792 # Create deadline for timeout tracking:
12893 deadline = Deadline . start ( timeout )
12994
13095 # Handle immediate timeout (non-blocking):
13196 if deadline == Deadline ::Zero && !@resolved
132- raise Async :: TimeoutError , "Promise wait not resolved!"
97+ return false
13398 end
13499
135100 # Wait with deadline tracking:
@@ -139,11 +104,67 @@ def wait(timeout: nil)
139104
140105 # Check if deadline has expired before waiting:
141106 if remaining <= 0
142- raise Async :: TimeoutError , "Promise wait timed out!"
107+ return false
143108 end
144109
145110 @condition . wait ( @mutex , remaining )
146111 end
112+
113+ return true
114+ end
115+
116+ # Wait for the promise to be resolved (without raising exceptions).
117+ #
118+ # If already resolved, returns immediately. Otherwise, waits until resolution or timeout.
119+ #
120+ # @parameter timeout [Numeric | Nil] Maximum time to wait. If nil, waits indefinitely. If 0, returns immediately if not resolved.
121+ # @returns [Boolean] True if the promise is resolved, false if timeout expires
122+ def wait? ( timeout : nil )
123+ unless @resolved
124+ @mutex . synchronize do
125+ # Increment waiting count:
126+ @waiting += 1
127+
128+ begin
129+ # Wait for resolution if not already resolved:
130+ unless @resolved
131+ if timeout . nil?
132+ wait_indefinitely
133+ else
134+ unless wait_with_timeout ( timeout )
135+ # We don't want to race on @resolved after exiting the mutex:
136+ return nil
137+ end
138+ end
139+ end
140+ ensure
141+ # Decrement waiting count when done:
142+ @waiting -= 1
143+ end
144+ end
145+ end
146+
147+ return @resolved
148+ end
149+
150+ # Wait for the promise to be resolved and return the value.
151+ #
152+ # If already resolved, returns immediately. If rejected, raises the stored exception.
153+ #
154+ # @returns [Object] The resolved value.
155+ # @raises [Exception] The rejected or cancelled exception.
156+ # @raises [Async::TimeoutError] If timeout expires before the promise is resolved.
157+ def wait ( ...)
158+ resolved = wait? ( ...)
159+
160+ if resolved . nil?
161+ raise TimeoutError , "Timeout while waiting for promise!"
162+ elsif resolved == :completed
163+ return @value
164+ elsif @value
165+ # If we aren't completed, we should have an exception or cancel reason stored:
166+ raise @value
167+ end
147168 end
148169
149170 # Resolve the promise with a value.
@@ -155,8 +176,8 @@ def resolve(value)
155176 @mutex . synchronize do
156177 return if @resolved
157178
158- @value = value
159179 @resolved = :completed
180+ @value = value
160181
161182 # Wake up all waiting fibers:
162183 @condition . broadcast
@@ -174,8 +195,8 @@ def reject(exception)
174195 @mutex . synchronize do
175196 return if @resolved
176197
177- @value = exception
178198 @resolved = :failed
199+ @value = exception
179200
180201 # Wake up all waiting fibers:
181202 @condition . broadcast
@@ -184,20 +205,16 @@ def reject(exception)
184205 return nil
185206 end
186207
187- # Exception used to indicate cancellation.
188- class Cancel < Exception
189- end
190-
191208 # Cancel the promise, indicating cancellation.
192209 # All current and future waiters will receive nil.
193210 # Can only be called on pending promises - no-op if already resolved.
194- def cancel ( exception = Cancel . new ( "Promise was cancelled!" ) )
211+ def cancel ( exception = Cancel . new ( "Promise cancelled!" ) )
195212 @mutex . synchronize do
196213 # No-op if already in any final state
197214 return if @resolved
198215
199- @value = exception
200216 @resolved = :cancelled
217+ @value = exception
201218
202219 # Wake up all waiting fibers:
203220 @condition . broadcast
0 commit comments