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

时间:2016-07-13 13:35:55

标签: javascript asynchronous promise ocaml js-of-ocaml

我们可以使用js_of_ocaml来包装JS函数,从而在OCaml中调用它。当JS函数是异步的时(即包括promises并花费时间),我无法做一个有用的例子。

我要包装的异步JS函数JSfun如下所示。变量x在2秒后设置为"here",这是我想要返回的值。

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很难模仿这种链接。要将JSfun包装在OCaml中,似乎我们必须使用:

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,但返回值不存在。

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

那么有没有人有解决方案,想法或解决方法?

1 个答案:

答案 0 :(得分:1)

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

代码看起来像这样(某些类型可能过于笼统):

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