# 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 ```c #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: ```c 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: ```c // 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 ```c 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: ```c 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.