We could use js_of_ocaml to wrap a JS function and thus call it within OCaml. I cannot make a working example when the JS function is asynchronous (ie, includes promises and takes time).
The asynchronous JS function JSfun
I want to wrap is as follows. The variable x
is set to "here"
after 2 seconds, and this is the value I want to return.
function JSfun() {
var x = "before";
return new Promise(function(resolve, reject) {
setTimeout(function() {
append("inside setTimeout");
x = "here";
resolve(x);
}, 2000);
})
}
We could successfully call JSfun
in JS and get "runJS here"
as expected:
function runJS() {
JSfun().then(function(res) {
append("runJS " + res)
})
}
However, it is hard to imitate this chaining by OCaml. To wrap JSfun
in OCaml, it seems that we have to use:
Js.Unsafe.global##.OCamlcall := Js.wrap_callback
(fun _ ->
let m = Js.Unsafe.fun_call (Js.Unsafe.js_expr "JSfun") [||] in
Js.string ((Js.to_string m) ^ " Via OCaml")
);
And I don't have other idea than calling like this:
function runOCaml() {
var res = OCamlcall("abc");
append(res);
}
Unsurprisingly, it does not work: we do see "inside setTimeout"
printed, which proves JSfun
has been called, but the return value was not there.
Here is the jsfiddle. I also make a working example of wrapping synchronous JS function. In OCaml, the wrapping is:
Js.Unsafe.global##.OCamlcallSync := Js.wrap_callback
(fun _ ->
let m = Js.Unsafe.fun_call (Js.Unsafe.js_expr "JSfunSync") [||] in
Js.string ((Js.to_string m) ^ " Via OCaml")
);
So does anyone have a solution, an idea or a workaround?
If your js function is asynchronous, your OCaml counterpart should also be async, i.e. it should use Lwt or Async. So, for Lwt you would probably use Lwt.task
to create sleeping thread and wakener, then pass callback with wakener to Promises' .then
method, and return sleeping thread from your function.
The code would look like this (some types are probably too general):
class type promise =
object
method then_ : ('a -> 'b) callback -> 'b meth
end
let ocamlcall () =
let t, w = Lwt.task () in
let wakeup = Js.wrap_callback (fun r -> Lwt.wakeup t r) in
let (promise : promise Js.t) = Js.Unsafe.fun_call jsfunc [||] in
let () = ignore @@ promise##then_ wakeup in
t
Note: I haven't tested this code, but it should give your general idea how to interact with async js functions.
You can also want to check how jsoo itself works with jsonp, for example: https://github.com/ocsigen/js_of_ocaml/blob/master/lib/jsonp.ml