是否可以包装一个异步 JS 函数并在 OCaml 中使用它?

Is it possible to wrap an asynchronous JS function and use it in OCaml?

我们可以使用 js_of_ocaml 来包装一个 JS 函数,然后在 OCaml 中调用它。当 JS 函数是异步的(即,包括承诺并需要时间)时,我无法给出一个工作示例。

我要包装的异步JS函数JSfun如下。变量 x 在 2 秒后设置为 "here",这就是我想要 return.

的值
function JSfun() {
  var x = "before";
  return new Promise(function(resolve, reject) {
    setTimeout(function() {
      append("inside setTimeout");
      x = "here";
      resolve(x);
    }, 2000);
  })
}

我们可以在 JS 中成功调用 JSfun 并按预期获得 "runJS here":

function runJS() {
  JSfun().then(function(res) {
    append("runJS " + res)
  })
}

但是,OCaml 很难模仿这种链接。要在 OCaml 中包装 JSfun,似乎我们必须使用:

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")
    );

除了这样调用我没有别的想法:

function runOCaml() {
  var res = OCamlcall("abc");
  append(res);
}

不出所料,它不起作用:我们确实看到打印了 "inside setTimeout",这证明 JSfun 已被调用,但 return 值不存在。

这里是jsfiddle。我还制作了一个包装同步 JS 函数的工作示例。在 OCaml 中,包装是:

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")
    );

那么有人有解决方案、想法或解决方法吗?

如果您的 js 函数是异步的,那么您的 OCaml 对应函数也应该是异步的,即它应该使用 Lwt 或 Async。因此,对于 Lwt,您可能会使用 Lwt.task 创建睡眠线程和唤醒器,然后将带有唤醒器的回调传递给 Promises 的 .then 方法,并从您的函数中 return 睡眠线程。

代码如下所示(某些类型可能过于笼统):

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

注意:我没有测试过这段代码,但它应该让您大致了解如何与异步 js 函数交互。

您还可以查看 jsoo 本身如何与 jsonp 一起工作,例如:https://github.com/ocsigen/js_of_ocaml/blob/master/lib/jsonp.ml