Next: Asynchronous calls, Previous: Synchronous calls, Up: Objects [Contents][Index]
The central data type abstracting asynchronous computations in Goblins is a special object called a promise. A promise is a representation of the result of an ongoing or future computation. Once a promised computation is completed, it can be either fulfilled, indicating success and carrying the value resulting from the computation; or broken, indicating that the computation failed and carrying an error (or other arbitrary value) conventionally explaining why.
Goblins promises are conceptually similar to JavaScript promises which may be familiar from other contexts. The most important difference is that Goblins promises support sending messages to the objects to which they will resolve, called “promise pipelining”. See Promise pipelining for more information.
Goblins provides (goblins actor-lib joiners)
and (goblins
actor-lib let-on)
to help flatten out code using
promises. See Let-On; see Joiners.
Typically, Goblins code will not need to manually create and fulfill promises unless it is interfacing with an outside system. However, even if a user never needs to create and fulfill promises directly, it is important to understand the process and its mechanisms to understand promises and asynchronous programming.
On a fundamental level, promises are created alongside their resolvers:
Return a promise and its associated resolver in that order as a
values
object.
(define-values (a-promise a-resolver) (spawn-promise-and-resolver))
The promise portion of a promise pair is what should be returned when
beginning an asynchronous computation. It can be passed to on
to access the result. $
can also return the value of a
resolved promise, but it cannot listen asynchronously for resolution,
limiting its usefulness for this case.
The resolver should be kept private and is used to fulfill or break a promise. It can respond to two messages using standard Goblins method syntax (see Methods):
fulfill val
: Successfully fulfill the promise with val.
break problem
: Indicate promise failure because of
problem, conventionally an exception object.
The best way to understand promises is to use them, so let’s use them. A new store has just opened in town, and it wants to celebrate its 100th customer. We can model it like so:
;; Imports we'll need (use-modules (goblins) (goblins actor-lib cell) (goblins actor-lib methods) (goblins vat)) (define (^store _bcom) ;; Spawn a promise pair representing the wait for the 100th customer (define-values (100th-promise 100th-resolver) (spawn-promise-and-resolver)) ;; Keep track of the customer count in a cell (define-cell customer-count 0) (methods ;; Hand out the promise for the 100th customer ((listen) 100th-promise) ;; When a customer shops, increment the customer counter. ((shop) (let ((new-count (1+ ($ customer-count)))) ($ customer-count new-count) (when (= new-count 100) ($ 100th-resolver 'fulfill "Woohoo! 100th customer!!"))))))
You can then listen to this actor’s promise with on
:
;; Spawn a vat (define a-vat (spawn-vat)) ;; Spawn the store actor (define store (with-vat a-vat (spawn ^store))) ;; Listen for the 100th customer (with-vat a-vat (on ($ store 'listen) ;; When the 100th customer enters, announce it (lambda (message) (display message) (newline)))) ;; Quickly simulate 100 customers (with-vat a-vat (for-each (lambda _ ($ store 'shop)) (iota 100))) ;; => "Woohoo! 100th customer!!"
Next: Asynchronous calls, Previous: Synchronous calls, Up: Objects [Contents][Index]