joyride 2025-10-10

Is there a better way to do JS for await, than constructing a JS function like this?

(defn collect-lm-response [response]
  (let [collector (js/Function. "response"
                    "return (async () => {
                       const chunks = [];
                       for await (const chunk of response.text) {
                         chunks.push(chunk);
                       }
                       return chunks.join('');
                     })();")]
    (collector response)))

(defn create-memory-from-context! [context]
  (p/let [[model] (vscode/lm.selectChatModels 
                    #js {:vendor "copilot" :family "gpt-4o"})]
    (when model
      (let [prompt (str "Create memory entry from: " context)
            messages [(vscode/LanguageModelChatMessage.User prompt)]
            token-source (vscode/CancellationTokenSource.)]
        (p/let [response (.sendRequest model (clj->js messages) 
                                       #js {} (.-token token-source))
                result (collect-lm-response response)]
          {:success true :memory result})))))
It’s when I collect the response streamed from an AI agent, where the examples use for await…

@pez Have you tried p/doseq?

Will try that. Thanks!

I contributed that to promesa :)

1

hmm, no p/doseq works on sync iterables of promises. chatgpt gave me this hint:

(defn consume-async-iterable [it]
  (let [iter (.values it)]
    (p/loop []
      (p/let [res (.next iter)]
        (if (.-done res)
          (js/console.log "Done!")
          (do
            (js/console.log "Value:" (.-value res))
            (p/recur)))))))

Thanks. IMO better than a JS string 😃

I’ll give it a try later today.

I don't think this works either, but the hint that an asynciterable delivers elements asynchronously is new to me. I should support for await in squint, before I thought it could just be written as for + an inner await.

Cool! Should SCI also support this, or can something be added to promesa that would make it more accessible to achieve. Dunno if p/doseq can be extended to support it?

it would be nice if p/doseq could be extended

This might work:

(defn iterate []
  (let [g (gen)
        iter ((aget g js/Symbol.asyncIterator))
        results (atom [])]
    (p/loop []
      (p/let [v (.next iter)]
        (if (.-done v) @results
          (do (swap! results conj (.-value v))
            (p/recur)))))))

it returns a promise with the iterated values

g is the async stream here

maybe you need p/recur btw

instead of just recur

@pez Finally figured out the SCI version. When you copy/paste this in a SCI env with promesa it works:

(def my-fn (js/Function "return (async function* makeAsyncIterable() {
  for (let i = 1; i <= 3; i++) {
    await new Promise(r => setTimeout(r, 1000));
    yield i;
  }
})"))

(def async-generator ((my-fn)))

(defn iterate [generator]
  (let [iter (.call (unchecked-get generator js/Symbol.asyncIterator) generator)
        results (atom [])]
    (js/console.log iter)
    (p/loop []
      (p/let [v (.next iter)]
        (if (.-done v) @results
          (do
            (js/console.log (.-value v))
            (swap! results conj (.-value v))
            (p/recur)))))))

(p/let [my-values (iterate async-generator)]
  (prn my-values))

note that iterate contains some js/console.logs that you may want to get rid of

Amazing. Maybe I can add to joyride.core?

then you will have a new problem. a good name ;)

😂 1

Is async-iterate a good name?

(defn consume-lm-response [response]
  (p/let [chunks (async-iterate (.-text response))]
    (apply str chunks)))

I don't think iterate is a good name since this function just collects all of them in one go

async-iterator-seq or so?

Makes sense. I’ll go with async-iterator-seq

🎉 1

Where do I see console.logs emitted from the joyride repl?

wait i got it, it's the vscode dev tools

Yes. I think println goes to the Calva repl output, if the Calva repl is connected.

I am actually planning on making it more predictable where some of the output goes. But js/console.log I won’t try to meddle with.