Можно ли обернуть асинхронную функцию JS и использовать ее в OCaml?

Мы могли бы использовать js_of_ocaml, чтобы обернуть JS-функцию и, таким образом, вызвать ее в OCaml. Я не могу сделать рабочий пример, когда функция JS является асинхронной (то есть включает в себя обещания и требует времени).

Асинхронная функция JS JSfun Я хочу завернуть это следующим образом. Переменная x установлен в "here" через 2 секунды, и это значение, которое я хочу вернуть.

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

Мы могли бы успешно позвонить JSfun в JS и получить "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 ответ

Решение

Если ваша функция js асинхронная, ваш аналог OCaml также должен быть асинхронным, то есть использовать Lwt или Async. Итак, для Lwt вы, вероятно, будете использовать Lwt.task создать спящий поток и wakener, а затем передать обратный вызов с 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

Другие вопросы по тегам