///|
pub async fn[T, E : Error] suspend(
f : ((T) -> Unit, (E) -> Unit) -> Unit
) -> T raise E = "%async.suspend"
///|
pub fn async_run(f : async () -> Unit) -> Unit = "%async.run"
///|
/// # Safety
///
/// You should always `Promise::wait` on any `Promise` you own to attach
/// exception handlers, as long as you are responsible for the control flow
/// (i.e. this `Promise` is not handled by an external JavaScript API).
/// This makes sure that when the operation in the this `Promise` errs out,
/// the error is caught by the MoonBit runtime.
#external
pub type Promise
///|
extern "js" fn Promise::wait_ffi(
self : Promise,
on_ok : (Value) -> Unit,
on_err : (Value) -> Unit
) -> Unit =
#| (self, on_ok, on_err) => self.then((t) => on_ok(t), (e) => on_err(e))
///|
pub async fn Promise::wait(self : Promise) -> Value raise {
suspend(fn(k, ke) { Promise::wait_ffi(self, k, fn(e) { ke(Error_(e)) }) })
}
///|
/// # Safety
///
/// You should always `Promise::wait` on the result of this function to attach
/// exception handlers, as long as you are responsible for the control flow
/// (i.e. the resulting `Promise` is not handled by an external JavaScript API).
/// This makes sure that when `op` errs out, the error is caught by the MoonBit runtime.
///
/// If you don't care about the result of the operation, you can use `spawn_detach` instead.
pub fn[T] Promise::unsafe_new(op : async () -> T raise) -> Promise {
Promise::new_ffi(fn() { Value::cast_from(op()) })
}
///|
extern "js" fn Promise::new_ffi(op : async () -> Value raise) -> Promise =
#| (op) => new Promise((k, ke) => op(k, ke))
///|
pub fn[T, E : Error] spawn_detach(op : async () -> T raise E) -> Unit {
async_run(fn() {
try op() |> ignore catch {
_ => ()
}
})
}
///|
/// # Note
/// The return type is guaranteed to be a `Promise` of an `Array`, but we omit that detail for simplicity.
pub extern "js" fn Promise::all(promises : Array[Promise]) -> Promise = "(ps) => Promise.all(ps)"
///|
/// Wraps each given `async fn` in a `Promise` and waits for all of them to resolve.
pub async fn[T] async_all(ops : Array[async () -> T raise]) -> Array[T] raise {
async_all_raw(ops.map(fn(op) { async fn() raise { Value::cast_from(op()) } })).map(
Value::cast,
)
}
///|
async fn async_all_raw(
ops : Array[async () -> Value raise]
) -> Array[Value] raise {
Promise::all(ops.map(Promise::unsafe_new)).wait().cast()
}
///|
pub fn async_test(op : async () -> Unit raise) -> Unit {
async_run(async fn() {
op() catch {
e => {
println("ERROR in `async_test`: \{e}")
panic()
}
}
})
}