Snapchat / djinni

A tool for generating cross-language type declarations and interface bindings. Djinni's new home is in the Snapchat org.
Apache License 2.0
173 stars 50 forks source link

[Future] [Wasm] Catch exceptions and reject promise #101

Closed bkotsopoulossc closed 2 years ago

bkotsopoulossc commented 2 years ago

Motivation

Suppose I have an async function that returns a djinni future, and for whatever reason an exception is thrown on the synchronous path (before kicking off any async code paths).

djinni::future<int> foo() {
  if (someThing) {
    throw std::runtime_error("failed");
  }

  djinni::Promise<int> promise;
  auto future = promise.get_future();

  // ... promise is used on an async call path and set to a value or exception

  return future;
}

This throw site could be changed to:

if (someThing) {
  return djinni::Promise::reject(std::runtime_error("failed"));
}

But suppose we were calling some synchronous code that we don't control, and it throws inside. We could try-catch around it, but it's still something the programmer has to remember:

try {
  checkSomeThing();
} catch (const std::exception& e) {
  return djinni::Promise::reject(std::current_exception());
}

Ask

Specifically for JS < -> Wasm, can we have a wrapper that catches exceptions thrown when invoking an async function, and return them as a rejected promise instead? We already have wrappers that catch exceptions to convert between C++ and JS errors, but they aren't aware of async functions with futures available.

What does JS do?

If we have the following JS code (link to playground), with an async function throwing, the exception is implicitly captured into the state of the promise, and only is re-thrown if the promise is awaited.

async function foo() {
    throw new Error("failed");
}

function runWithoutAwait() {
    try {
        foo();
    } catch (e) {
        console.log("runWithoutAwait: caught", e);
    }
}

async function runWithAwait() {
    try {
        await foo();
    } catch (e) {
        console.log("runWithAwait: caught", e);
    }
}

runWithAwait();

runWithoutAwait();

-- 
> "runWithAwait: caught",  failed