|
54 | 54 | (= 'sci.impl.async-await/finally op) |
55 | 55 | (= 'sci.impl.async-await/resolve op)))))) |
56 | 56 |
|
57 | | -(defn- ensure-promise-result |
58 | | - "Ensure async function body returns a promise. |
59 | | - Wraps the last expression in js/Promise.resolve if not already a promise." |
60 | | - [body-exprs] |
61 | | - (if (empty? body-exprs) |
62 | | - (list (wrap-promise nil)) |
63 | | - (let [exprs (vec body-exprs) |
64 | | - last-idx (dec (count exprs)) |
65 | | - last-expr (nth exprs last-idx)] |
66 | | - (if (promise-form? last-expr) |
67 | | - body-exprs |
68 | | - (assoc exprs last-idx (wrap-promise last-expr)))))) |
69 | | - |
70 | 57 | (declare transform-async-body) |
71 | 58 |
|
72 | 59 | (defn transform-do |
|
117 | 104 |
|
118 | 105 | :else form)) |
119 | 106 |
|
120 | | -(defn transform-loop* |
121 | | - "Transform loop* with await into a recursive promise-returning function. |
122 | | - (loop* [x 0] (if (< x 3) (do (await p) (recur (inc x))) x)) |
123 | | - => |
124 | | - ((fn loop-fn [x] (if (< x 3) (.then p (fn [_] (loop-fn (inc x)))) (js/Promise.resolve x))) 0)" |
125 | | - [ctx locals bindings body] |
126 | | - (let [loop-fn-name (gensym "loop_fn__") |
127 | | - pairs (partition 2 bindings) |
128 | | - param-names (mapv first pairs) |
129 | | - init-values (map second pairs) |
130 | | - ;; Transform init values - they may contain await |
131 | | - transformed-inits (doall (map #(transform-async-body ctx locals %) init-values)) |
132 | | - ;; Add params to locals |
133 | | - body-locals (into locals param-names) |
134 | | - ;; Replace recur with loop function call |
135 | | - body-with-replaced-recur (map #(replace-recur % loop-fn-name) body) |
136 | | - ;; Transform the body using transform-do to properly chain promises |
137 | | - transformed-body (transform-do ctx body-locals body-with-replaced-recur) |
138 | | - ;; Ensure promise result |
139 | | - promised-body (if (promise-form? transformed-body) |
140 | | - transformed-body |
141 | | - (wrap-promise transformed-body)) |
142 | | - ;; Build the loop function |
143 | | - loop-fn (list 'fn* loop-fn-name param-names promised-body) |
144 | | - ;; Build the loop call, chaining any promise init values |
145 | | - make-loop-call (fn [init-vals] |
146 | | - (wrap-promise (apply list loop-fn init-vals)))] |
147 | | - ;; If any init value is a promise, chain them |
148 | | - (if (some promise-form? transformed-inits) |
149 | | - (letfn [(chain-inits [vals-before remaining] |
150 | | - (if (seq remaining) |
151 | | - (let [v (first remaining)] |
152 | | - (if (promise-form? v) |
153 | | - (let [await-sym (gensym "init__")] |
154 | | - (promise-then v (list 'fn* [await-sym] |
155 | | - (chain-inits (conj vals-before await-sym) |
156 | | - (rest remaining))))) |
157 | | - (chain-inits (conj vals-before v) (rest remaining)))) |
158 | | - (make-loop-call vals-before)))] |
159 | | - (chain-inits [] transformed-inits)) |
160 | | - (make-loop-call transformed-inits)))) |
161 | | - |
162 | 107 | (defn transform-let* |
163 | 108 | "Transform let* with await calls into .then chains." |
164 | 109 | [ctx locals bindings body] |
|
190 | 135 | (list 'let* (vec acc-bindings) transformed-body)) |
191 | 136 | transformed-body))))) |
192 | 137 |
|
| 138 | +(defn transform-loop* |
| 139 | + "Transform loop* with await into a recursive promise-returning function. |
| 140 | + Wraps init values in let* to preserve sequential scoping (each init sees previous bindings). |
| 141 | +
|
| 142 | + (loop* [x 0] (if (< x 3) (do (await p) (recur (inc x))) x)) |
| 143 | + => |
| 144 | + (let* [x 0] |
| 145 | + ((fn loop-fn [x] (if (< x 3) (.then p (fn [_] (loop-fn (inc x)))) (js/Promise.resolve x))) x))" |
| 146 | + [ctx locals bindings body] |
| 147 | + (let [loop-fn-name (gensym "loop_fn__") |
| 148 | + pairs (partition 2 bindings) |
| 149 | + param-names (mapv first pairs) |
| 150 | + init-values (mapv second pairs) |
| 151 | + ;; Add params to locals for body transformation |
| 152 | + body-locals (into locals param-names) |
| 153 | + ;; Replace recur with loop function call |
| 154 | + body-with-replaced-recur (map #(replace-recur % loop-fn-name) body) |
| 155 | + ;; Transform the body using transform-do to properly chain promises |
| 156 | + transformed-body (transform-do ctx body-locals body-with-replaced-recur) |
| 157 | + ;; Ensure promise result |
| 158 | + promised-body (if (promise-form? transformed-body) |
| 159 | + transformed-body |
| 160 | + (wrap-promise transformed-body)) |
| 161 | + ;; Build the loop function |
| 162 | + loop-fn (list 'fn* loop-fn-name param-names promised-body) |
| 163 | + ;; Build the loop call - uses param names since they'll be bound by let* |
| 164 | + loop-call (wrap-promise (apply list loop-fn param-names)) |
| 165 | + ;; Build let* bindings: [param1 init1 param2 init2 ...] |
| 166 | + let-bindings (vec (interleave param-names init-values))] |
| 167 | + ;; Wrap in let* and transform - this handles sequential scoping and promise chaining |
| 168 | + (transform-let* ctx locals let-bindings (list loop-call)))) |
| 169 | + |
193 | 170 | (defn transform-try |
194 | 171 | "Transform try/catch/finally with await into Promise .catch/.finally chains. |
195 | 172 | Only uses promise chains if there's actually an await somewhere." |
|
420 | 397 |
|
421 | 398 | :else body))))) |
422 | 399 |
|
| 400 | +(defn- ensure-promise-result |
| 401 | + "Ensure async function body returns a promise. |
| 402 | + Wraps the last expression in js/Promise.resolve if not already a promise." |
| 403 | + [body-exprs] |
| 404 | + (if (empty? body-exprs) |
| 405 | + (list (wrap-promise nil)) |
| 406 | + (let [exprs (vec body-exprs) |
| 407 | + last-idx (dec (count exprs)) |
| 408 | + last-expr (nth exprs last-idx)] |
| 409 | + (if (promise-form? last-expr) |
| 410 | + body-exprs |
| 411 | + (assoc exprs last-idx (wrap-promise last-expr)))))) |
| 412 | + |
423 | 413 | (defn transform-async-fn-body |
424 | 414 | "Transform async function body expressions and ensure result is a promise. |
425 | 415 | This is the main entry point for async function transformation." |
|
0 commit comments