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.