Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 19 additions & 3 deletions docs/concepts/javascript2oaf.md
Original file line number Diff line number Diff line change
Expand Up @@ -173,7 +173,7 @@ For function-based type checking there are a set of functions prefixed with "is"
| tprintErr | Outputs a string based on a provided template using data from an object to stderr with a new-line |
| tprintErrnl| Outputs a string based on a provided template using data from an object to stderr without a new-line |

## Logging
## Logging

| Function | Description |
|----------|-------------|
Expand All @@ -189,6 +189,10 @@ For function-based type checking there are a set of functions prefixed with "is"
| startLog | Starts logging to a default or specified [OpenAF channel](OpenAF-Channels.md) with automatic housekeeping |
| stopLog | Stops logging to a default of specified [OpenAF channel](OpenAF-Channels.md) after a startLog function has been previously executed. |

## Error handling helpers

OpenAF provides utility helpers to improve diagnostics and recoverability when exceptions are raised. The most notable helper added for the v20250721 release is `$err(exception, rethrow, returnStr, code)`. It formats JavaScript and Java exceptions consistently, prints the stack trace (respecting the `OAF_ERRSTACK` flag) and, when source information is available, highlights the code lines around the failure so troubleshooting is easier. By default `$err` writes the formatted message to the console, but you can set `returnStr=true` to obtain the formatted text programmatically or `rethrow=true` to propagate the original exception after logging it.

## Navigating arrays and maps

There are a set of libraries includes to help to "navigate" through javascript arrays and maps:
Expand All @@ -201,11 +205,23 @@ There are a set of libraries includes to help to "navigate" through javascript a

## Utility

_tbc_
The `af` namespace continues to aggregate convenience helpers for day-to-day scripting. Two recent additions are:

| Function | Description |
|----------|-------------|
| `af.nvl(value, default)` | Returns `value` when it is defined and non-null, or `default` otherwise. It is a concise alternative to common null/undefined coalescing patterns when working with optional data sources. |
| `af.swap(array, index1, index2)` | Returns a new array where the elements at `index1` and `index2` have been exchanged. The original array is validated and left untouched, making it safer to reorganise data that is being shared between functions or promises. |

## Parallel execution

_tbc_
Asynchronous utilities received multiple improvements that embrace Java virtual threads while keeping the familiar OpenAF APIs:

* `$do(fn)` continues to run `fn` on the managed worker pool and immediately returns an `oPromise` chain.
* `$doV(fn)` mirrors `$do`, but executes the promise callbacks on Java virtual threads when they are available. This is ideal for IO-bound workloads that benefit from lightweight concurrency without exhausting classic thread pools.
* `$sync()` returns an object exposing `run(fn)` which serialises access to the provided function by using a `ReentrantLock`. It is a simple way to protect shared resources that are updated from multiple promises or channel callbacks without manually instantiating locks.
* `$queue(initialItems)` creates a thread-safe FIFO queue backed by `ConcurrentLinkedQueue`, exposing helpers such as `add`, `poll`, `peek`, `has`, and `size`. Passing an initial array enqueues it immediately, offering an easy coordination primitive for producer/consumer flows.

These utilities can be combined: for example, producers can push work with `$queue().add(...)`, consumers can fetch it with `$doV` to process in virtual threads, and any critical sections can be guarded with `$sync().run(...)`.

## OPack specific

Expand Down
8 changes: 8 additions & 0 deletions docs/concepts/oJob.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,14 @@ To run it you would just execute:
ojob myrecipe.yaml
````

## Embeeded documentation for an oJob

Starting with OpenAF v20250721 the `ojob` CLI can render the oJob documentation directly. Two new options are available:

* `ojob -reference` renders a human-friendly overview of jobs, arguments and dependencies using the standard console output (with colours when supported).
* `ojob -mdreference` emits the same reference in Markdown.


In the next chapters will go through how you can add arguments to each job (e.g. how many eggs, how much sugar, …), how you can have jobs chains (e.g. job “Get ingredients” → “Make cake” → “Serve cake” for just a to do “Make cake”), how you can make a production line of cakes with array arguments and more.

# Jobs
Expand Down
56 changes: 54 additions & 2 deletions docs/guides/advanced/executing-code-asynchronously.md
Original file line number Diff line number Diff line change
Expand Up @@ -81,5 +81,57 @@ var allPromises = $doAll(arrayOfPromises)
$doWait(allPromises);
````

Ok. But won't that just lunch a ton of threads "burning" down my machine? No. You don't need to worry about that. Underneath it will create a set of threads giving the number of identified compute cores and reuse them. So some promises will actually be waiting for others to finish and to have a thread available for them to execute.
In OpenAF there is also the function parallel4array and others to make it easy (and smarter) to asynchronously process an array since a promise for each might not actually give the best performance. But that will be the topic of another post.
Ok. But won't that just lunch a ton of threads "burning" down my machine? No. You don't need to worry about that. Underneath it will create a set of threads giving the number of identified compute cores and reuse them. So some promises will actually be waiting for others to finish and to have a thread available for them to execute.
In OpenAF there is also the function parallel4array and others to make it easy (and smarter) to asynchronously process an array since a promise for each might not actually give the best performance. But that will be the topic of another post.

## Virtual threads with `$doV`

OpenAF v20250721 introduces `$doV`, a drop-in replacement for `$do` that runs promise executors on Java virtual threads whenever they are available. Virtual threads are extremely lightweight and are ideal when you need to spawn hundreds or thousands of concurrent IO tasks without overwhelming the classic worker pools. Using `$doV` is as simple as swapping the function call:

````javascript
var promise = $doV(() => {
var body = io.readUrl("https://example.org/service");
return process(body);
}).then(saveResult)
.catch($err);

$doWait(promise);
````

From an API perspective `$do` and `$doV` share the same chaining semantics, so you can pick the appropriate implementation per workload without refactoring your orchestration.

## Coordinating concurrent work with `$sync` and `$queue`

More helpers were added to simplify coordination between asynchronous tasks:

* `$sync()` creates a small wrapper that serialises access to a critical section. It is perfect to guard code that updates shared state from multiple promises.
* `$queue(initialItems)` yields a thread-safe queue that supports `add`, `poll`, `peek`, `has`, and other utility methods backed by `ConcurrentLinkedQueue`.

Combining both helpers allows you to build producer/consumer flows without leaving JavaScript:

````javascript
var work = $queue();
var syncStats = $sync();
var stats = [];

// Producer
$doV(() => {
files.forEach(file => work.add(file));
});

// Consumers
var workers = [];
for (var i = 0; i < 4; i++) {
workers.push($doV(() => {
var next;
while ((next = work.poll()) !== null) {
var report = analyse(next);
syncStats.run(() => stats.push(report));
}
}));
}

$doWait($doAll(workers));
````

In the previous snippet producers and consumers use virtual threads to keep latency low, while the `$sync` block ensures the shared `stats` collection stays consistent.
36 changes: 35 additions & 1 deletion docs/howto/Use-files.md
Original file line number Diff line number Diff line change
Expand Up @@ -107,4 +107,38 @@ io.readLinesNDJSON("abc123.json", (line) => { sprint(line); });
````javascript
var logline = { date: now(), level: "INFO", message: "a log message" };
io.writeLineNDJSON("abc123.json", logline);
````
````

## Work with LZ4 compressed files

OpenAF v20250721 adds native helpers for LZ4 compression, making it easy to exchange compressed payloads without external tools:

* `io.lz4(data)` compresses a map, array, string, byte array or stream using the LZ4 frame format and returns the compressed bytes.
* `io.unlz4(bytes)` performs the inverse operation, returning the original map/string when the input was compressed with `io.lz4`.
* `io.writeFileLZ4Stream(path)` opens an output stream that writes LZ4-compressed data directly to disk. It is ideal when you want to compress large files on the fly.
* `io.readFileLZ4Stream(path)` yields an input stream that decompresses LZ4 content while you read it, avoiding the need to load the entire file in memory.

Example usage:

````javascript
// Compress an object and persist the bytes
var payload = { id: 1, values: [1, 2, 3] };
var compressed = io.lz4(payload);
io.writeFileBytes("payload.lz4", compressed);

// Later, decompress it back
var restored = io.unlz4(io.readFileBytes("payload.lz4"));

// Write compressed content incrementally
var outStream = io.writeFileLZ4Stream("huge.log.lz4");
try {
logs.forEach(line => outStream.write((line + "\n").getBytes("UTF-8")));
} finally {
outStream.close();
}

// Read compressed content as a stream
var inStream = io.readFileLZ4Stream("huge.log.lz4");
// ...use the Java InputStream as needed...
inStream.close();
````
Loading