是否可以包装一个异步 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
我们可以使用 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