JsPromise and Promise
libwasm has two promise types, and they are not interchangeable. Confusing them is how a lang=d script compiles and then either never resumes or aborts on the first throw.
JsPromise!T — a handle to a browser Promise
struct JsPromise(T = Any) (types.d) is a JsHandle. fetch, Response.json, and anything the bindings return as a JS Promise is this type. The methods are then and error (not catch, which is a D keyword).
window().fetch(url).then((Response res) {
return res.json();
}).then((JSON body) {
this.update.msg = body["ok"].get!string;
}).error((Any reason) {
this.update.msg = "failed";
});then takes a nothrow delegate. On JsPromise!void the delegate has no argument; otherwise it takes T. The return type of the delegate becomes the next JsPromise’s T. error always takes Any and returns JsPromise!void. There is no finally yet (the source comments // todo: .Finally).
all, allSettled, and any take a JsHandle[], pack the handles, and return JsPromise!Any. They are the JS combinators, not D fibers.
{#await job} in svelte-d is a JsPromise handle plus @visible pending / then / catch children. App.ready calls wireAwait once per job. On a module the etcimon/binaryen fork asyncified, that is sequential job.await; after rewind D reads libwasmAwaitFailed() and fills {:then v} from libwasmAwaitValue() or {:catch e} from libwasmAwaitError(). Stock Binaryen keeps the .then / .error pair so the page still settles; those callbacks note the Any handle (libwasmNoteAwaitOk / libwasmNoteAwaitFail) then read the same strings. The first job keeps await_then / await_catch; later jobs use await_*_<job>. Two {e} / {v} bindings uniquify to eP / eP2. The promise must already be a JsPromise. A D Promise!T is the wrong type.
Promise!(T, E) — an in-process D promise
libwasm.promise is CyberShadow’s ae implementation, Promises/A+ with D deviations. T may be void. catch is except. finally is finish. Fulfill and reject are ordinary methods, not constructor executors. Attempts to fulfill a non-pending promise assert. In debug builds, a resolved promise that is destroyed without a handler reports a leak to stderr (via the console hooks _start installed).
The router uses Optional!(Promise!void) for @entering / @leaving callbacks so a route can wait before the next iterate. That is a D promise. It is not a JsPromise. You cannot .then a browser fetch with this type, and you cannot await it on the wasm-eh cell.
.await after rewind, never around a landing pad
void await(T)(auto ref T promise) {
libwasm_await__void(promise.handle.handle);
}libwasm_await__void is an import that Binaryen --asyncify rewrites so the wasm stack can unwind while JS waits. Official Binaryen 123 and 132 still Flatten-crash on try_table. The etcimon/binaryen fork (bun run build-wasm-opt → binaryen-svelte-d) asyncifies the same wasm-eh module. svelte-d wasm uses that fork when it finds it; otherwise it -Ozs and {#await} stays on .then.
JS always resolves the import so rewind happens (otherwise ScopedPool dtors never run). A rejected Promise is stored as libwasmAwaitFailed() / window.__svelteDLastAwait; a resolve stores the then-value the same way. D reads those flags after .await returns, assigns {:then v} from libwasmAwaitValue() or {:catch e} from libwasmAwaitError(), and flips the matching @visible flags. A try that wraps the import is still unsound: rewind re-enters from the top and the personality slot is not part of Asyncify.
libwasmAwaitSupported() is the D view of asyncify_get_state. Printed wireAwait uses it to choose .await versus .then. A bare .await on a stock (not asyncified) module is still a silent no-op — that is why the printer checks first.
DevTools: debug-bridge rewrites wasm-function[N] and asyncify_start_unwind frames, and __svelteDRewriteError runs on a wrapExportFn throw so an async stack still names the orig .svelte when the name section can join.
Personality and nothrow
Printed structs are nothrow: @safe:. then delegates are nothrow. A throw inside a then that escapes the delegate is not a {#await} catch branch; it is an abort unless you try/catch in that same function. See memory, EH, CSS, HMR and wasm-eh.