-
Hey, I've implemented a very basic/hacky version of pub fn fetch(_this: &JsValue, args: &[JsValue], ctx: &mut Context<'_>) -> JsResult<JsValue> {
let url = args
.get(0)
.ok_or_else(|| JsNativeError::typ().with_message("fetch: URL required"))?
.as_string()
.ok_or_else(|| JsNativeError::typ().with_message("fetch: URL must be a string"))?
.to_std_string_escaped();
let data = futures::executor::block_on(async {
reqwest::Client::builder()
.user_agent(env!("CARGO_PKG_NAME"))
.build()
.expect("building the client should not fail")
.get(&url)
.send()
.await?
.error_for_status()?
.json()
.await
})
.map_err(|e| JsNativeError::error().with_message(e.to_string()))?;
let json = JsValue::from_json(&data, ctx)?;
let obj = ObjectInitializer::new(ctx)
.function(
unsafe { NativeFunction::from_closure(move |_, _, _| Ok(json.clone())) },
"json",
0,
)
.build();
let promise = JsPromise::new(
|resolvers, context| {
resolvers
.resolve
.call(&JsValue::undefined(), &[obj.into()], context)?;
Ok(JsValue::undefined())
},
ctx,
)?;
Ok(promise.into())
} After registering the function via ctx.register_global_callable("fetch", 0, NativeFunction::from_fn_ptr(Fetch::fetch))` I'm able to run JS code like this, which is awesome: const json = await fetch("https://example.com").then(r => r.json()); However, using pub async fn fetch(
_this: &JsValue,
args: &[JsValue],
ctx: &mut Context<'_>,
) -> JsResult<JsValue> {
let url = args
.get(0)
.ok_or_else(|| JsNativeError::typ().with_message("fetch: URL required"))?
.as_string()
.ok_or_else(|| JsNativeError::typ().with_message("fetch: URL must be a string"))?
.to_std_string_escaped();
let data = reqwest::Client::builder()
.user_agent(env!("CARGO_PKG_NAME"))
.build()
.expect("building the client should not fail")
.get(&url)
.send()
.and_then(|r| r.json())
.await
.map_err(|e| JsNativeError::error().with_message(e.to_string()))?;
let json = JsValue::from_json(&data, ctx)?;
let obj = ObjectInitializer::new(ctx)
.function(
unsafe { NativeFunction::from_closure(move |_, _, _| Ok(json.clone())) },
"json",
0,
)
.build();
Ok(obj.into())
} I've been trying to pass that function to There's only a single example of |
Beta Was this translation helpful? Give feedback.
Replies: 2 comments 1 reply
-
Yeah, the main problem here is that you need to use the boa/boa_engine/src/object/builtins/jspromise.rs Lines 287 to 303 in 1e10f07 Just substitute let future = async move {
let data = async_reqwest().await;
NativeJob::new(move |context| match JsValue::from_json(&data, context) {
Ok(v) => resolvers.resolve.call(&JsValue::undefined(), &[v], context),
Err(e) => {
let e = e.to_opaque(context);
resolvers.reject.call(&JsValue::undefined(), &[e], context)
}
})
} |
Beta Was this translation helpful? Give feedback.
-
Thanks. That definitely helped. Unfortunately, |
Beta Was this translation helpful? Give feedback.
Yeah, the main problem here is that you need to use the
&mut Context
reference after awaiting, which catches the mutable reference and makes the whole future not static. Fortunately, we added an escape hatch to the async API where you can de-sugar the utility function into its internal representation as a future job, which removes this limitation with the downside of some increased verbosity:boa/boa_engine/src/object/builtins/jspromise.rs
Lines 287 to 303 in 1e10f07