I also sometimes wish I could attach async handler to promises. I agree 
that you can emulate this with (thread ...) calls, but sometimes you really 
want a fully non-blocking behavior. If your promises are delivered by an 
asynchronous, non-blocking system, it feels too bad to end up with a 
blocking program just because of the moment you deref' the promise, at 
least from a performance viewpoint.

I personally made a hack that lets you attach async handlers to promises 
while avoiding blocking many threads waiting for each of them to complete. 
It consists of having a single (logical) thread scan the set of promises 
from time to time and calling the handlers on those who have completed. See 
this 
gist <https://gist.github.com/vvvvalvalval/005eaef8986e20f37d0d> :

I'm curious to know what you think of this approach.

Le dimanche 29 décembre 2013 23:12:15 UTC+1, Cedric Greevey a écrit :
>
> Actually, one simple way to use core.async to asynchronously watch a 
> future or a promise, or *any* other blocking-derefable thingy, for 
> delivery, is just to throw a (thread (do-something-with @thingy)) in your 
> code somewhere. The thread will block until the thingy is ready, and then 
> do whatever. Meanwhile your usual code can just @thingy like it would have 
> anyway somewhere, without its behavior there being changed.
>
> This can be done anywhere the thingy is visible. What can't be done with 
> any of these (including core.async used natively) is to catch errors 
> without being able to put (or amend) a try block into the producer end of 
> things. This suggests that anything where consumers might want notification 
> of failure should employ a separate error channel (core.async) or out of 
> band values (future, promise, core.async) produced in a try block. For 
> example (future (try (/ a b) (catch ArithmeticException _ :error))) where 
> the consumer can look for :error instead of a number when derefing (in this 
> case, this would be caused by the edge case b = 0).
>
>
>
> On Sun, Dec 29, 2013 at 5:01 PM, Cedric Greevey <cgre...@gmail.com 
> <javascript:>> wrote:
>
>> On Sun, Dec 29, 2013 at 4:24 PM, larry google groups <
>> lawrenc...@gmail.com <javascript:>> wrote:
>>
>>> And here is where the greatest disappointment arrives: neither future 
>>> <http://clojuredocs.org/clojure_core/clojure.core/future> nor promise 
>>> <http://clojuredocs.org/clojure_core/clojure.core/promise> in Clojure 
>>> supports listening for completion/failure asynchronously. ... As much 
>>> as I love Clojure concurrency primitives like STM and agents, futures feel 
>>> a bit underdeveloped. Lack of event-driven, asynchronous callbacks that are 
>>> invoked whenever futures completes (notice that add-watch 
>>> <http://clojuredocs.org/clojure_core/clojure.core/add-watch> doesn't 
>>> work futures - and is still in alpha) greatly reduces the usefulness of a 
>>> future object. We can no longer:
>>>
>>>    - map futures to transform result value asynchronously
>>>    - chain futures
>>>    - translate list of futures to future of list
>>>    - ...and much more, see how Akka does it 
>>>    
>>> <http://nurkiewicz.blogspot.no/2013/02/javautilconcurrentfuture-basics.html>
>>>     and Guava to some extent 
>>>    
>>> <http://nurkiewicz.blogspot.no/2013/02/advanced-listenablefuture-capabilities.html>
>>>  
>>>
>>> That's a shame and since it's not a technical difficulty but only a 
>>> missing API, I hope to see support for completion listeners soon. 
>>>
>>>
>>>
>>> So, I am curious if we will see support for completion listeners. Or is 
>>> there a feeling that stuff like core.async has addressed some of this and 
>>> nothing more is needed?
>>>
>>
>> Well, core.async directly subsumes the entire functionality of promises. 
>> And adds watch capability:
>>
>> (let [c (chan)] ; instead of (promise)
>>   (go ; prefer thread if the long calculation is REALLY long
>>     (>! c (long calculation here)) ; instead of (future (deliver ...))
>>     (close! c))
>>   (let [d (go
>>              (let [x (<! c)]
>>                (println "Special delivery!" x) ; Watch!
>>                x))]
>>     (<!! d))) ; (instead of @c)
>>
>> The channel here is used just like a promise: the first go asynchronously 
>> does some calculation and delivers a result to it, then closes it, which 
>> results in behavior like a delivered promise, i.e. it can't be delivered to 
>> again. One could just use (<!! c) to grab the result (blocking until 
>> available), like @some-promise. The second go block shows one bit of added 
>> power: you can add an asynchronous watch for the result to appear. The go 
>> block takes it from c as soon as it's available, executes the watch (in 
>> this case a simple println), and then evaluates to the delivered value. 
>> Letting [d (go ...)] results in d being bound to a channel that will get 
>> this value pushed onto it (and then get closed) when the go block 
>> terminates, and (<!! d) blocks the main thread until this happens and then 
>> evaluates to the result.
>>
>> The one thing "missing" is that whereas you can @some-promise repeatedly 
>> to retrieve the value once it's delivered, you can't (<!! d) repeatedly 
>> without getting the delivered value once and then nils thereafter. But you 
>> could change it slightly to combine channels *with* promise:
>>
>> (let [c (chan)]
>>   (go
>>     (>! c (long calculation here))
>>     (close! c))
>>   (let [d (promise)]
>>     (go
>>       (let [x (<! c)]
>>         (println "Special delivery!" x)
>>         (deliver d x)))]
>>     @d))
>>
>> Now d is a promise, but it's delivered by a go block that can also 
>> asynchronously watch for completion. But leaving d as a channel might be 
>> better. You can always put the result in another kind of container once 
>> it's delivered, and deref that repeatedly, and by leaving d a channel you 
>> can use alt! and timeout on this channel, for example, to add timeout 
>> behavior to your checking for delivery.
>>
>> Futures used locally can be "watched asynchronously" trivially: instead 
>> of (future (long calculation here)) you have (future (let [x (long 
>> calculation here)] (hey-x-got-finished! x) x)). The (hey-x-got-finished! x) 
>> call will take place in the future's thread. If you don't want it blocking 
>> the main thread (i.e. you want (hey-x-got-finished! x) able to run 
>> concurrently with whatever derefs the future, instead of the latter maybe 
>> being blocked longer) then you'd use (future (let [x (long calculation 
>> here)] (future (hey-x-got-finished! x)) x)). :)
>>
>> That deals with watching for completion. Watching for error is as easy 
>> with future: wrap a try ... catch around the body inside (future ...). With 
>> promise/core.async, you need the try block in the producer's code, whereas 
>> core.async lets you add completion watching at the consumer end as easily 
>> as at the producer. (Even to a normal promise generated by, say, code you 
>> can't change: just (let [c (thread @p)] ...) to turn promise p into channel 
>> c that will get @p pushed onto it when p is delivered; thread is used 
>> instead of go because a blocking job in a go is not recommended. Wrap the 
>> @p in something that will do something with its argument and return its 
>> argument to add a completion watch, for values of "do something" that can 
>> of course include spawning further async processes.)
>>
>> Futures you get passed from code you don't control (never heard of this 
>> happening, but I suppose it *could* happen) can be treated analogously to 
>> promises where you control only the consumer: for error checking you're 
>> SOL, but for completion watching you just wrap the deref in core.async's 
>> (thread ...). You can then spawn alarm processes from in there upon 
>> delivery, use the channel returned by (thread ...) in alts with timeout, 
>> use the channel in a go, just do a blocking take from it, and etc.
>>  
>
>

-- 
You received this message because you are subscribed to the Google
Groups "Clojure" group.
To post to this group, send email to clojure@googlegroups.com
Note that posts from new members are moderated - please be patient with your 
first post.
To unsubscribe from this group, send email to
clojure+unsubscr...@googlegroups.com
For more options, visit this group at
http://groups.google.com/group/clojure?hl=en
--- 
You received this message because you are subscribed to the Google Groups 
"Clojure" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to clojure+unsubscr...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Reply via email to