Source: src/hql/transpiler/pipeline/transform/async-generators.ts
async-fn ::= '(' 'async' 'fn' name? params body... ')'
async-gen-fn ::= '(' 'async' 'fn*' name? params body... ')'
gen-fn ::= '(' 'fn*' name? params body... ')'
await-expr ::= '(' 'await' expr ')'
yield-expr ::= '(' 'yield' expr? ')'
yield-star ::= '(' 'yield*' expr ')'
for-await ::= '(' 'for-await-of' '[' binding iterable ']' body... ')'
HQL provides first-class support for asynchronous programming and generators, mapping directly to JavaScript's async/await, function*/yield, and async function* constructs.
;; Named async function
(async fn fetch-data [url]
(let response (await (js/fetch url)))
(await (.json response)))
;; Anonymous async function
(let fetcher (async fn [url]
(await (js/fetch url))))
;; Async with map parameters
(async fn connect {host: "localhost" port: 8080}
(await (establish-connection host port)))
(async fn fetch-data [url]
(let response (await (js/fetch url)))
(await (.json response)))
Compiles to:
async function fetchData(url) {
const response = await __hql_consume_async_iter(fetch(url));
return await __hql_consume_async_iter(response.json());
}
async WorksThe async keyword is a modifier that:
fn or fn*transformFn or transformGeneratorFn)async flag on the resulting IR function node via _setAsyncFlag()(await promise-expr)
await wraps its argument in __hql_consume_async_iter(), a runtime helper that:
(await (fetch-data url))
Compiles to:
await __hql_consume_async_iter(fetchData(url))
await requires exactly one argument. Zero or multiple arguments raise a ValidationError.
;; Named generator
(fn* range-gen [start end]
(var i start)
(while (< i end)
(yield i)
(= i (+ i 1))))
;; Anonymous generator
(let nums (fn* []
(yield 1)
(yield 2)
(yield 3)))
(fn* countdown [n]
(while (> n 0)
(yield n)
(= n (- n 1))))
Compiles to:
function* countdown(n) {
while (n > 0) {
yield n;
n = n - 1;
}
}
fn* WorksThe fn* form:
transformFn (regular function transformation)generator flag on the resulting IR nodeFunctionExpression, FunctionDeclaration, FnFunctionDeclaration, VariableDeclaration (with function init)(yield value) ;; yield a value
(yield) ;; yield undefined
(yield 42)
Compiles to:
yield 42
(yield)
Compiles to:
yield
yield takes at most one argument. More than one argument raises a ValidationError.
(yield* iterator-expr)
(yield* [1 2 3])
Compiles to:
yield* [1, 2, 3]
(fn* combined []
(yield* (range-gen 1 5))
(yield* [10 20 30]))
yield* requires exactly one argument.
(async fn* fetch-pages [urls]
(for-of [url urls]
(let response (await (js/fetch url)))
(yield (await (.json response)))))
(async fn* paginate [start max-pages]
(var page start)
(while (<= page max-pages)
(let data (await (fetch-page page)))
(yield data)
(= page (+ page 1))))
Compiles to:
async function* paginate(start, maxPages) {
let page = start;
while (page <= maxPages) {
const data = await __hql_consume_async_iter(fetchPage(page));
yield data;
page = page + 1;
}
}
(for-await-of [item async-iterable]
body...)
(for-await-of [page (paginate 1 10)]
(process-page page))
Compiles to:
for await (const page of paginate(1, 10)) {
processPage(page);
}
| Rule | Error |
|---|---|
async with no argument | "async requires a function form" |
async with non-fn/fn* | "async currently supports 'fn' and 'fn*' definitions" |
await with != 1 argument | "await requires exactly one argument" |
yield with > 1 argument | "yield takes at most one argument" |
yield* with != 1 argument | "yield* requires exactly one argument" |
try blocks containing await automatically get async IIFE wrapperstry blocks containing yield automatically get generator IIFE wrappersawait arguments pass through __hql_consume_async_iter for async iterator handling| Feature | fn | async fn | fn* | async fn* |
|---|---|---|---|---|
| Named | Yes | Yes | Yes | Yes |
| Anonymous | Yes | Yes | Yes | Yes |
| Positional params | Yes | Yes | Yes | Yes |
| Map params | Yes | Yes | Yes | Yes |
| Multi-arity | Yes | Yes | Yes | Yes |
| Type annotations | Yes | Yes | Yes | Yes |
| TCO (self) | Yes | No | No | No |
| TCO (mutual) | Yes | No | Yes | No |
yield | No | No | Yes | Yes |
yield* | No | No | Yes | Yes |
await | No | Yes | No | Yes |
src/hql/transpiler/pipeline/transform/async-generators.tssrc/hql/transpiler/pipeline/transform/function.ts__hql_consume_async_iter in src/common/runtime-helper-impl.tstests/unit/async.test.ts, tests/unit/generator.test.ts