Late to the party here, but there's a ebook available that talks directly about this problem in Pharo. And gives an exact solution. I know it works because I've used it.
https://books.pharo.org/booklet-ConcurrentProgramming/
This solution is lifted right from that book. I didn't invent it.
The solution involves adding two message to the Class BlockClosure.
BlockClosure >> promise
^ self promiseAt: Processor activePriority
and:
BlockClosure >> promiseAt: aPriority
"Answer a promise that represents the result of the receiver
execution
at the given priority."
| promise |
promise := Promise new.
[ promise value: self value ] forkAt: aPriority.
^ promise
Then creating a Promise Class.
Object subclass: #Promise
instanceVariableNames: 'valueProtectingSemaphore value hasValue'
classVariableNames: ''
package: 'Promise'
Promise >> initialize
super initialize.
valueProtectingSemaphore := Semaphore new.
hasValue := false
Promise >> hasValue
^ hasValue
Promise >> value
"Wait for a value and once it is available returns it"
valueProtectingSemaphore wait.
valueProtectingSemaphore signal. "To allow multiple requests for
the value."
^ value
Promise >> value: resultValue
value := resultValue.
hasValue := true.
valueProtectingSemaphore signal
I used this when implementing an MQTT data reader. I would request the latest message from the Broker and return a promise. That allowed me to loop around the promise and not block all the other processes running in the image.
Works like a charm. And while it took me a little effort to wrap my head around how it works, it's not that bad.
You use it by putting your action in a block and sending it the promise method
myPromise := [broker readMessage ] promise.
And you can test it:
(myPromise hasValue) ifTrue: [
myObject processMsg: (myPromise value)
]
You'll want to put your task in it's own thread to keep it from blocking the rest of your image.