Time and Timers
Sending requests after a delay
If an actor wishes to send itself a message immediately, it can use
Actor me
.
Actor me log: 'Here''s a message'.
The result is a Promise
of an eventual reply.
To send a message at some point in the future, use Squeak’s built-in
future
mechanism. For example, this will cause the current actor’s
behavior to be sent the log: 'Tick!'
message in one second (1,000
milliseconds):
(Actor me future: 1000) log: 'Tick!'.
Again, a Promise
of an eventual reply is the result.
This causes the message to be enqueued after 1,000 ms have elapsed - it may not be received and processed until later.
The same technique works for sending delayed messages to other actors,
too. Here, we send doSomething
via the ActorProxy
p
after a
second has gone by:
(p future: 1000) doSomething
The same technique works for asynchronous, synchronous or blocking transient proxies for an actor:
(p async future: 1000) doSomething "Later sends an asynchronous request to p"
(p sync future: 1000) doSomething "Later sends a synchronous request to p"
"NB. the `sync` option is just like '(p future: 1000) doSomething'"
(p blocking future: 1000) doSomething "Later sends a blocking request to p"
Confusingly, in all three cases, a Promise
is returned. The promise
is resolved in different ways depending on whether async
, sync
(the default), or blocking
is used:
-
For
async
, the promise is resolved withnil
as soon as the asynchronous request is sent on top
’s actor. -
For
sync
, the promise (call it “promise A”) is linked to the promise resulting from the synchronous call (“promise B”). Once promise B, in turn, settles, promise A will take on its state, either resolved or rejected. -
For
blocking
, the UI process will block untilp
’s actor replies. Once this happens, the promise will be resolved with the reply value.
The reason the UI process is involved is that Squeak’s delayed-execution mechanism itself always works via the UI process.
Because the delayed message send happens on the UI process, and
promise resolution also happens on the UI process, execution of
resolution/rejection handlers also happens on the UI process. Make
sure to use #bindActor
if a resolution handler needs to execute in a
particular actor’s context. See
here for more
information.
Waiting for a reply with a timeout
Promises resulting from invoking a request object are
in fact instances of ActorPromise
, which augments the built-in
Squeak Promise
with a new method, ActorPromise >> #waitFor:ifTimedOut:
.
anActorProxy someSlowRequest waitFor: 1000 ifTimedOut: [ "..." ]
Sending someSlowRequest
to an ActorProxy
starts the request
process as usual, and the #someSlowRequest
method starts running in
the other actor. Meanwhile, the calling actor receives an
ActorPromise
, which is immediately sent #waitFor:ifTimedOut:
with
a timeout of 1,000ms and a timeout handler block.
The call to ActorPromise >> #waitFor:ifTimedOut:
behaves differently
depending on what happens. Its result will be
-
the result of
someSlowRequest
, if that method returns a value before the timeout fires; -
a
BrokenPromise
, if the called actor terminates abnormally before the timeout fires; or -
the result of the timeout handler block, if the timeout fires before any of the other two possibilities come to pass.
The timeout handler block can take any action, including signalling an error, returning nil, or returning any other useful value.
The special case of a timeout handler block returning nil can be
written []
:
anActorProxy someSlowRequest waitFor: 1000 ifTimedOut: []