EsPromise
Description
A way to produce <EsFuture> objects and to complete them later with a value or error.
Most of the time, the simplest way to create a future is to just use one of the creational APIs to capture the result of a single asynchronous computation:
EsFuture on: [self doSomething. result]
or, if the future represents the result of a sequence of asynchronous computations, they can be chained using EsFuture>>#then: or similar functions on <EsFuture>
doStuff
^self someAsyncOperation then: [:result | self someOtherAsyncOperation: result]
If you do need to create a Future from scratch — for example, when you're converting a callback-based API into a Future-based one — you can use an <EsPromise> as follows:
[class AsyncOperation]
instVar: promise := EsPromise new;
AsyncOperation>>doOperation
self startOperation.
^promise future "Send future object back to client."
AsyncOperation>>finishOperation: result
"Something calls this when the value is ready."
promise complete: result
AsyncOperation>>errorHappened: error
"If something goes wrong, call this."
promise completeError: error
Instance State
• future: <EsFuture> future that is completed by this promise
Class Methods
async
Creates a new asynchronous promise
The promise completes the future asynchronously. That means that
handlers registered on the future are not called immediately when
#complete: or #completeError: is called. Instead the handlers are
delayed until later using EsAsyncTaskScheduler>>scheduleTask:.
Answers:
<EsPromise>
new
Creates a new promise
The general workflow for creating a new future is to 1) create a
new promise, 2) hand out its future, and, at a later point, 3) invoke
either #complete: or #completeError:
The promise completes the future asynchronously. That means that
handlers registered on the future are not called immediately when
#complete: or #completeError: is called. Instead the handlers are
delayed until later using EsAsyncTaskScheduler>>scheduleTask:.
Example:
promise := EsPromise new.
self handOut: promise future.
... 'At some later point' ...
promise complete: 'completion value'
Answers:
<EsPromise>
sync
Completes the future synchronously.
This method should be avoided unless the completion of the future is
known to be the final result of another asynchronous operation. If in doubt
use the default promise creational api [EsPromise new].
Using a normal, asynchronous, promise will never give the wrong
behavior, but using a synchronous promise incorrectly can cause
otherwise correct programs to break.
A synchronous promise is only intended for optimizing event
propagation when one asynchronous event immediately triggers another.
It should not be used unless the calls to #complete: and #completeError:
are guaranteed to occur in places where it won't break <EsFuture> invariants.
Completing synchronously means that the promise's future will be
completed immediately when calling the #complete: or #completeError:
method on a synchronous promise, which also calls any handlers
registered on that future.
Completing synchronously must not break the rule that when you add a
handler on a future, that handler must not be called until the code
that added the handler has completed. For that reason, a synchronous
completion must only occur at the very end (in 'tail position') of another
synchronous event, because at that point, completing the future immediately
is be equivalent to returning to the event loop and completing the future in the
next scheduleTask:
Example:
promise := EsPromise sync.
promise future timeout: 5 then: [-1].
promise future then: [:v | self assert: v equals: 42].
promise complete: 42.
Answers:
<EsPromise>
Instance Methods
complete
Complete the future with `nil`.
@Note: Calling EsPromise>>complete: or EsPromise>>completeError:
must be done only once.
All listeners on the future are informed about the value
complete:
Complete the future with the supplied value @aValue.
@aValue can be any resolved <Object> or an <EsFuture>
If @aValue is an <EsFuture>, then this promise will wait
for the future to complete, and then complete with the same
success or error result.
@Note: Calling EsPromise>>complete: or EsPromise>>completeError:
must be done only once.
All listeners on the future are informed about the value
Arguments:
aValue - <Object>
completeError:
Complete the future with the supplied error @anError
Completing a future with an error is an indication that an exception
was thrown while trying to resolve the value.
If @anError is a future, the future is used as the error value.
If you want to complete with the result of the future, use
[promise complete: aFuture]
@Note: Calling EsPromise>>complete: or EsPromise>>completeError:
must be done only once.
Arguments:
aValue - <Object>
completeError:stackTrace:
Complete the future with the supplied error @anError
and @aStackTrace
Completing a future with an error is an indication that an exception
was thrown while trying to resolve the value.
If @anError is a future, the future is used as the error value.
If you want to complete with the result of the future, use
[promise complete: aFuture]
@Note: Calling EsPromise>>complete: or EsPromise>>completeError:
must be done only once.
Arguments:
aValue - <Object>
aStackTrace - <EsAsyncStackTrace>
future
Answer the future that is completed by this promise
Answers:
<EsFuture>
isCompleted
Whether the future has been completed.
Reflects whether #complete: or #completeError: has been called.
A 'true' value doesn't necessarily mean that listeners of this future
have been invoked yet, either because the completer usually waits until
a later #scheduleTask: to propagate the result, or because #complete:
was called with a future that hasn't completed yet.
When this value is `true`, #complete: and #completeError: must not be
called again
Answers:
<Boolean>
Last modified date: 04/21/2022