This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
2023-11-21
Channels
- # announcements (1)
- # beginners (20)
- # biff (5)
- # calva (43)
- # cider (5)
- # clj-commons (7)
- # clj-kondo (11)
- # clojure (58)
- # clojure-brasil (1)
- # clojure-denmark (1)
- # clojure-europe (27)
- # clojure-nl (1)
- # clojure-norway (13)
- # clojure-uk (2)
- # clojurescript (71)
- # data-science (32)
- # datalevin (6)
- # datomic (19)
- # emacs (1)
- # gratitude (3)
- # honeysql (8)
- # hoplon (15)
- # hyperfiddle (3)
- # introduce-yourself (1)
- # lsp (19)
- # malli (4)
- # nbb (7)
- # other-lisps (5)
- # practicalli (1)
- # re-frame (14)
- # releases (1)
- # ring-swagger (1)
- # squint (118)
- # xtdb (9)
- # yada (2)
Hey guys, I'm using cljs.test
(in node) to test async code.
My tests look something like that:
(deftest single-anchor-created
(async done
(go
(let [before {}
after {:id (str (random-uuid))
:extends ["ID"]}]
(<! (match-trees before after))
(done)))))
match-trees
makes some HTTP calls to remote services (handling promises with <p!
).
When an error occurs, I'm getting this generic misinformed error:
/builds/dev/dev-apps/app/.shadow-cljs/builds/test/dev/out/cljs-runtime/cljs/core.cljs:11623
(let [e (js/Error. message)]
^
Error: Promise error
at new cljs$core$ExceptionInfo (/builds/dev/dev-apps/app/.shadow-cljs/builds/test/dev/out/cljs-runtime/cljs/core.cljs:11623:11)
at Function.cljs$core$IFn$_invoke$arity$3 (/builds/dev/dev-apps/app/.shadow-cljs/builds/test/dev/out/cljs-runtime/cljs/core.cljs:11650:1)
at /builds/dev/dev-apps/app/.shadow-cljs/builds/test/dev/out/cljs-runtime/cljs/core/async/interop.cljs:26:26
at processTicksAndRejections (node:internal/process/task_queues:96:5)
error Command failed with exit code 1.
I'm compiling with shadow-cljs
and running with node: yarn shadow-cljs compile test && node out/node-tests.js
:test {:target :node-test
:output-to "out/node-tests.js"}
How can I configure something to print the message of the promise error?
I did add some (.catch promise)
to print some errors, but it's not ideal to write it everywhere in my code...you are going to need to handle errors in some way. if you are only using core.async for promise handling I strongly suggest to drop that entirely and just use promises directly
from js world (specifically jest) I remember that a test that throws an error (`async-await`) will print the error message
So I need to wrap each call with try/catch or .catch? Maybe a wrapper around the deftest
?
every call no, where you are interfacing with the promise. one place should be enough.
I have just went over the project again, there are plenty of promises. Do you mean that each call to a promise should add a .catch
?
I don't understand how will it be a single place
well I cannot see your code, so I don't know how you are interfacing with promises in the first place
core.async puts this Promise error
onto the channel, it then throws it when reading from the channel. so if you don't have a try/catch anywhere then it just makes it into your go code
I think we have deviated a bit into core.async, there's something I probably don't undertand.
(defn c []
(go (<p! rejected-promise)))
(defn b []
(go (<! (c))))
(defn a []
(go (<! (b))))
if I wrap a
in try/catch
, the rejected promise will be caught?
(defn a []
(try (go (<! (b)))
(catch :default e (println e)))
It's just a nightmare to use only promises, we have dependent calls, and it's a callback (`.then`) hell...
(defn c []
(go (<p! rejected-promise)))
(defn b []
(go (let [res (<! (c))]
(js/console.log "result from c" res)
res)))
(defn a []
(go (<! (b))))
try that to check what you result you get from c
. I'm fairly sure thats just nil
, so the exception is lost in c already, it doesn't travel further
OK I see.
Regarding js-await
, it does create nested calls (pyramid like).
I guess we cannot achieve something like js async await:
const f = async () => {
const a = await promise()
const b = await promise(a)
const c = await promise (b)
}
where the caller of f
:
try{ await f() } catch { ... }
something like that:
(defn my-async-fn [foo]
(let [the-result (js-await (promise-producing-call foo))
the-result-2 (js-await (doing-something-with-the-result the-result))]
(calc the-result-2))
(try (js-await (my-async-fn))
(catch ...))
(defn my-async-fn [foo]
(js-await [the-result (promise-producing-call foo)]
(js-await [the-result-2 (doing-something-with-the-result the-result)]
(calc the-result-2)
)))
(js-await [the-result (my-async-fn)]
(catch err
...))
await
also creates nested code, its just visually not nested. semantically it still is
IMO this code that you posted
const f = async () => {
const a = await promise()
const b = await promise(a)
const c = await promise (b)
}
Is even easier to write with just .then
:
(defn f []
(-> (promise)
(.then promise)
(.then promise))
;; To make it behave the same as the JS fn above.
(js/Promise.resolve))
I just wanted to make it behave in the exactly same way. :) So it's the JS function's fault it doesn't return the original.
I'm fairly certain that the async fn above only resolves once all the awaits are done, even if with no own return values. yours resolves immediately
(defn f []
(-> (promise)
(.then promise)
(.then promise)
(.then (fn [dismissed-result] js/undefined))))
The problems I'm trying to overcome are: ā¢ A single catch block in the top scope ā¢ Using multiple promises results like:
const a = await foo()
const b = await bar(a)
const c = await baz(a, b)
If you chain promises (returning a new promise from a .then
handler would also be chained), you only have to use .catch
once.
When you break a chain, you have to add another .catch
.
To catch any errors thrown while a promise is being handled when there's no corresponding .catch
, you can listen to the unhandledrejection
event globally.
The JS code above can be written as:
(let [ap (foo)
bp (.then ap bar)
cp (.then (js/Promise.all [ap bp])
(fn [[a b]]
(baz a b)))]
(.then cp
(fn [c]
...)))
Alternatively:
(-> (foo)
(.then (fn [a]
(.then (bar a) (fn [b] [a b])))
(.then (fn [[a b]]
(baz a b)))
(.then (fn [c]
...)))
In both cases, a single .catch
at the end will be enough to catch any error that was thrown during any of the mentioned calls. But just as with the case of some top-level (try ... (catch ...))
, you might lose a bit of helpful context.
Also note that chaining .then
after .catch
with handle the result of the catch handler as if it were a regular value. So if you want for a caught exception to propagate all the way through to the top, don't use .catch
in the middle or rethrow the error, perhaps with additional context.in my mind js async/await works exactly like the js-await
macro looks. as in execute something, then execute the body or catch. there is no magic chaining going on in JS either, so technically the above translation is wrong
(.then (foo)
(fn [a]
(.then (bar a)
(fn [b]
(.then (baz a b)
(fn [c]
...
))))))
is what actually happens basicallyNot sure I follow this time. In which scenario my code above wouldn't behave in the same way? > but nobody likes that nesting Yeah, that's why I immediately shied away from that. :D
yes, but then you have to do extra work to shuttle the results around. if you just have functions that capture those its fine.
Right. I was translating only the behavior, without any attempts to make the translation mechanically perfect.
note that you can use .then
and js-await
interchangeably since its just raw promises without any kind of wrapper. both work seamlessly together
@U057T406Y15 in my opinion you could achieve your goal and get much nicer looking code by using the promesa
library.
const a = await foo()
const b = await bar(a)
const c = await baz(a, b)
becomes:
(:require [promesa.core :as p])
(p/catch
(p/let [a (foo)
b (bar a)
c (baz a b)]
... resolve test condition here ...
)
(fn [err] ... do something with error ...))
If you literally just want to catch the top level error so you can print it then there is a feature in Node to do that:
process.on('unhandledRejection', error => {
console.log('unhandledRejection', error.message);
});
Any unhandled Promise rejection will arrive there.thanks @UUSQUGUF3, I have just made the project to work so I won't try it right now, but I have written it aside (the promesa)
Anyone familiar with https://github.com/thi-ng/geom/blob/feature/no-org/org/examples/gl/webgl.org#example-1-2d-polygons and/or webgl here? I'd like to draw some polygons defined in code with one color per vertex, but all the examples i can find either use a uniform color for the entire shape, or forgo colors entirely and use textures. Any help or pointers in the right direction would be appreciated.
Definitely not an expert, but I'm pretty sure you have to either write a shader that does that or use a texture.
Any idea where to start on shaders? I only had a brief look as i've never written any before (all previous openGL work was with vertex and index buffers), and couldn't quite figure out how to structure them or access variables
And I'm pretty sure it has a bug.
I can replace :fixed-color
with :colors
, according to the sources. But seems that I have to make it a vector of vectors, and it fails later when
tries to fill a buffer with all those values.
@U0552GV2X32 Is this the expected image?
Just created an issue for
that also shows how to implement the above: https://github.com/thi-ng/geom/issues/99
@U2FRKM4TW Yes, cool! Technically i'm drawing polygons each of their own color, but your example is the more general case