-
Notifications
You must be signed in to change notification settings - Fork 203
Open
Description
It's not possible to catch an error in top-level async/await
JsResult run(Source source) throws InterruptedException, ExecutionException {
CompletableFuture<JsResult> finalResult = new CompletableFuture<>();
// Submit everything to the JS event loop
eventLoop.submit(() -> {
try {
Value result = context.eval(source); // Eval runs on JS thread
String state = "pending";
if (result.hasMember("state")) {
state = result.invokeMember("state").asString();
}
if ("fulfilled".equals(state)) {
finalResult.complete(JsResult.ok(result.invokeMember("value")));
} else if ("rejected".equals(state)) {
Value reason = result.invokeMember("reason");
finalResult.completeExceptionally(
new RuntimeException(reason.toString()));
} else if (result != null && result.hasMember("then")) {
// Promise: attach handlers
result.invokeMember("then", (ProxyExecutable) args -> {
// (2J) resolving fine
finalResult.complete(JsResult.ok(args.length > 0 ? args[0] : null));
return null;
}, (ProxyExecutable) args -> {
// (3J) not catching - bug???
Value jsError = args.length > 0 ? args[0] : null;
finalResult.complete(JsResult.error(jsError));
return null;
});
} else {
// Not a Promise, resolve immediately
finalResult.complete(JsResult.ok(result));
}
}catch (Throwable t) {
// (1J) Any immediate JS error
finalResult.complete(JsResult.error(t));
}
});
// Pump JS event loop until the result completes (or fails)
eventLoop.runUntil(finalResult::isDone);
// Stop the event loop now to avoid dangling tasks
close();
// Propagate exceptions to caller
return finalResult.get();
}in js:
// throw new Error('(1S) this can be caught alright in (1J)')
try {
await Promise.all([
read('test/fixture/index.js').then(()=>{
console.log('[🥇 greel] read index.js')
}),
read('test1/fixture/hello-world.js').then(()=>{
console.log('[🥇 greel] read hello-world.js')
})
])
}catch(er) {
console.log('[🥇 greel] ❌',er.message)
throw er // <--- (2) RETHROW ERROR HERE, CAUSES BUG: NOT CAUGHT
}
console.log('[🥇 greel] 🏁 Completed program')
async function read(path) {
var d=new Date
// await new Promise(r=>setTimeout(r,Math.random()*500))
console.log('[🥇 greel] Asking to read',path)
var result=await fs.read(path)
console.log(`[🥇 greel] ✅ read ${path} after ${(new Date).getTime()-d.getTime()}ms (${result.length}B)`)
}outputs
[🥇 greel] Asking to read test/fixture/index.js
{🗿 graal} Starting read test/fixture/index.js
{🗿 graal} 📖 Completed read test/fixture/index.js
[🥇 greel] Asking to read test1/fixture/hello-world.js
{🗿 graal} Starting read test1/fixture/hello-world.js
{🗿 graal} 📕 Failed read test1/fixture/hello-world.js
[🥇 greel] ✅ read test/fixture/index.js after 2289ms (918B)
[🥇 greel] read index.js
[🥇 greel] ❌ Failed to read file: File not found: test1/fixture/hello-world.js
so it's stalling. if there's an error before await on the top-level (1S), it catches it in java's try-catch (1J), but if it happens after await, the second (catchy) callback to "then" (3J) it's not being executed.
when there's no error, it successfully lands in the callback for .then in Java (2J)
[🥇 greel] Asking to read test/fixture/index.js
{🗿 graal} Starting read test/fixture/index.js
{🗿 graal} 📖 Completed read test/fixture/index.js
[🥇 greel] Asking to read test/fixture/hello-world.js
{🗿 graal} Starting read test/fixture/hello-world.js
{🗿 graal} 📖 Completed read test/fixture/hello-world.js
[🥇 greel] ✅ read test/fixture/index.js after 78ms (928B)
[🥇 greel] read index.js
[🥇 greel] ✅ read test/fixture/hello-world.js after 46ms (51B)
[🥇 greel] read hello-world.js
[🥇 greel] 🏁 Completed program
seems like a bug to me, but maybe i haven't set up global error listeners or smth?
Metadata
Metadata
Assignees
Labels
No labels