Today I've been developing some ClojureScript browser code that returns a promise. I typically C-x C-e to eval statements and get the response inside Emacs. I would like to look at the return value (e.g. browser fetches a response from API) but that is a promise. I suppose there is no technical stopper why CIDER couldn't wait that promise and then show me the value but it doesn't. Is there some existing solution or work-in-progress?
And no, it's not quite the same to print the stuff in the browser console, browser window, tap> into Portal etc. I do that if there are no other good ideas. I'm iterating the code in Emacs, I'd like to be fully there, fullscreen. Those other tools maybe work for some people, but I don't feel like it is the best developer experience for Emacs users.
I don't think there is, and it would probably have too many edge cases to have it bundled in cider. However it shouldn't be hard to make something work for your specific use-case. For example:
(defun my-cider-eval-with-deref ()
(interactive)
(cider-eval-last-sexp)
(let ((val (cider-last-sexp)))
(when (string-match-p "\\(future\\|delay\\|promise\\)" val)
(cider-interactive-eval
(concat "@" val)
(cider-interactive-eval-handler (current-buffer) (point))))))
When calling this function after this clojure code:
(delay (Thread/sleep 2000) 42)
It will first display the delay then the 42.So you are testing this with Clojure? That doesn't look like it would work in CLJS
Babashka actually 😅. Why wouldn't it work with cljs? You do need to adapt what the string-match-p looks for, and maybe how you deref the promise
This detail is in "how you deref the promise".
There is no deref in JS promises.
There is .then but then you would have to deliver the value to some place and inspect that some place later.
Or if you were in JS you'd like to use await and then do something with the value.
(concat "(.then " val " (fn [val] (def *deref-promise val))) *deref-promise")
Maybe? I forgot if you can do inline def in clojurescript, but if you can't then you can just use an atom for exampleBasically you would eval
(.then a-promise
(fn [val] (def *deref-promise val)))
*deref-promiseBut you will get the promise? Your fn should deliver the value somewhere and later you need to look at the value.
(cider-last-sexp) is always something that returns a promise, and .then also returns a promise, so the return value will always be the promise.
Unless in elisp one waits for something to happen to the promise via .then or so and only returns execution after that with the proper value.
Hmm you're right, I'll fire up a cljs repl and think about it. It's been a while since I've used promises
I'm sure it's not impossible for someone who is more familiar with elisp and what CIDER already provides to loop it or so
It's quite plausible that if you give this problem to ChatGPT, it will come back with some workaround. Something like what @dromar56 suggested, but with a .then callback that will eventually echo the result into the minibuffer.
That's pretty much what I already have, as in using this:
(concat "(.then " val " (fn [val] (def *deref-promise val))) *deref-promise")
Will show the 42 if you eval (js/Promise.resolve 42)I don't this works the way you think it works.
But if you eval something like:
(-> (js/Promise.resolve 42)
(.then (fn [val] (do-something-with-val))))
That then with do-something will get the value of the resolve first, and not pass it back.
In this case what we would need is to somehow add our own then first, like:
(-> (js/Promise.resolve 42)
(.then (fn [val] (def *dered-promise val) val))
(.then (fn [val] (do-something-with-val))))
But even this wouldn't work in a general way, as we could be evaluating (fn-that-returns-promise-with-a-then), and then we can't interpose our thenI don't this works the way you think it works.It's possible, but what do you mean in particular?
Try it with some delay.
My Javascript is very rusty to know how to do that 😅
I think there are actual CIDER experts on this channel so I don't have to try to shepherd a stochastic parrot 🙂
Here's your example here https://clojurescript.io/:
That just doesn't do it
So how one goes about polling the CLJS-world to figure out that the promise has been resolved
You can try shepherding the parrot towards a PR so that others benefit from it too! 🙂
BTW, curiously it goes inline with https://clojurians.slack.com/archives/C0617A8PQ/p1737400698101539. If there were a way to show print results in the code buffer, then printing the promise on callback would be an option too.
Sure, it should be orthogonal where it is printed but IMHO all the eval functions are a mess.
What's messy about them? You mean that there're too many specialized eval functions?
Well... we don't have to wait for it in cljs 🤷 but yeah, hopefully someone can chime in with a cleaner solution.
(defun wait-for-promise-resolution ()
(while (equal (nrepl-dict-get (cider-nrepl-sync-request:eval "(= *p :unresolved)") "value")
"true")
(message "Not yet resolved")
(sit-for 0.1))
(message "Resolved: %s" (nrepl-dict-get (cider-nrepl-sync-request:eval "*p") "value")))
(defun my-cider-eval-with-deref ()
(interactive)
(cider-eval-last-sexp)
(let ((val (cider-last-sexp)))
(when (string-match-p "\\(future\\|delay\\|promise\\)" val)
(cider-nrepl-sync-request:eval
(concat "(def *p :unresolved) (.then " val " (fn [val] (def *p val)))"))
(wait-for-promise-resolution)
(cider-interactive-eval "*p"
(cider-interactive-eval-handler (current-buffer) (point))))))
I wrote something similar but didn't know of sit-for
I think your version seems to not work in JS (because it won't match "Promise"), and it will also print the "promise" inline in the buffer and print the actual value in the minibuffer?
the runtime is chrome, via shadow-cljs
same for me
but it might not work depending on what exactly you are evaluating. For example if instead of that js block that creates promises, you eval a function that does, it might not work indeed
js/fetch currently, but wrapped by lambdaisland.fetch
If you eval it normally what does cider print? You would match it against that
#<js/Promise[~]>
if I just hard-code it to match then it works with the effect of printing slightly differently than the normal eval
The problem with these different eval commands is that one must override all of them that one ever uses 😅
Weird, the string match does seem to work: (string-match-p "\\(future\\|delay\\|promise\\)" "#<js/Promise[~]>") => 5. It's still very brittle, you should probably bind it to a specific key and use it only when you know you're evaluating a promise.
Or use nrepl-sync-request and check that the object is a promise with:
const isPromise = v => typeof v === 'object' && typeof v.then === 'function'
(apparently having a .then method is the only thing that differentiates a promise)Yeah, that's easier to work around but outputting the result is the other niggle.
The regular does this
(cider-interactive-eval "*p"
(when output-to-current-buffer (cider-eval-print-handler))
(cider-last-sexp 'bounds)
(cider--nrepl-pr-request-map))So the result ends up in the buffer with the file
However my attempts to use that pattern instead actually don't show results until I move a character
I don't understand how one is supposed to compose these
If you're passing a string to cider-interactive-eval you have to pass the eval handler explicitely, like this:
(cider-interactive-eval "*p"
(cider-interactive-eval-handler (current-buffer) (point)))
Because usually you don't pass a string, you pass a position in the buffer, so the eval-handler knows where to display the overlay. If you pass a string then it's up to you to tell it where is the point
But that doesn't actually do it
It only shows up in the minibuffer
The #<js/Promise[~]> ends up inline
I don't know what's the difference in our setups, it does appear at the right place (as in my last gif)
Are you running Emacs 29?
This is what it looks like for me
Ah that's what I thought you wanted 😅 Having the result similarly to where C-x C-e would put it
But it doesn't actually do that
It does show first the js/Promise, until it resolves
Yeah, that's not a problem if the result later replaces it
But the result goes to minibuffer instead, and if I move a character then it appears in the buffer too
Also it's not highlighted so something isn't working
Ah I see. I wrote the result in the message just for debugging purposes, you can remove this line: (message "Resolved: %s" (nrepl-dict-get (cider-nrepl-sync-request:eval "*p") "value")) As for why you need to move a character... I'm not sure
So now it's just missing the highlighting
Removing messages fixed the need to move
Looks like that inspector isn't liking it either
Inspector error for: {:status 200, :headers {"cache-control" "no-store", "content-length" "3544", "content-type" "application/transit+json; charset=utf-8"}, :body [{:application/workflow {:workflow/id 1, :workflow/type :workflow/default}, :application/external-id ...Inspector doesn't work for cljs
I guess not, but the highlighting should
Ah ok now I understand the matching. (cider-last-sexp) is the code, not the return value. One should match against the return value?
Ah you're right, that's a mistake
Thanks anyway so far. Have to think of how to compose this best with the other evals. I typically eval only inline or other buffer.
https://clojurians.slack.com/archives/C8NUSGWG6/p1737744185185759
https://clojurians.slack.com/archives/C02V9TL2G3V/p1737761710005069