Files
dusk/.claude/script-promises.md
T
2026-06-16 10:15:59 -05:00

2.6 KiB

Script -- Async Promises (scriptpromisepend_t)

Source: src/dusk/script/scriptpromisepend.h

See also: .claude/script.md


Overview

When a C module needs to resolve a JS Promise from an asynchronous C event (e.g. an asset finishing loading, a network response arriving), use scriptpromisepend_t. The pattern avoids heap allocation by using a fixed-size pending slot array declared in the module.


Declaring the pending array

#define MY_MODULE_PENDING_MAX 8
static scriptpromisepend_t MY_PENDING[MY_MODULE_PENDING_MAX];
static uint32_t MY_PENDING_COUNT = 0;

Add a pending promise

Called from the JS-facing function that returns the Promise:

jerry_value_t promise = jerry_create_promise();
scriptPromisePendAdd(
  MY_PENDING, &MY_PENDING_COUNT, MY_MODULE_PENDING_MAX,
  key,      // opaque void * used to match the resolve/reject later
  promise
);
return jerry_acquire_value(promise); // return a copy to the caller

The key should be a stable pointer that uniquely identifies the async operation -- e.g. an assetentry_t *, a network request handle, or a pointer to a fixed-size slot in the module.


Resolve or reject

Called when the C event fires, typically from moduleUpdate or an event callback:

// On success:
scriptPromisePendResolve(
  MY_PENDING, &MY_PENDING_COUNT,
  key, jerry_undefined()  // or a result value
);

// On failure:
jerry_value_t err = jerry_create_error(
  JERRY_ERROR_COMMON, (const jerry_char_t *)"reason"
);
scriptPromisePendReject(MY_PENDING, &MY_PENDING_COUNT, key, err);
jerry_release_value(err);

Both macros remove the slot from the pending array after settling.


Guard against double-submit

if(scriptPromisePendHas(MY_PENDING, MY_PENDING_COUNT, key)) {
  // already waiting -- return the existing promise or an error
}

Module teardown

Free all pending promises before cleaning up events or other state:

scriptPromisePendFreeAll(MY_PENDING, &MY_PENDING_COUNT);

This rejects all still-pending promises and resets the count to 0. Call it from the module's Dispose function, before any backing data (asset entries, event subscriptions) is torn down.


Design notes

  • MY_MODULE_PENDING_MAX sets a hard cap on concurrent async ops. Exceeding it is a runtime assertion -- size the array to the maximum realistic concurrency for the module.
  • The key is opaque (void *); the system does not dereference it. A raw integer cast to void * is fine if no pointer is available.
  • scriptUpdate() runs the JerryScript microjob queue each frame, which is what processes .then() chains after a resolve/reject.