OCaml/Node.JS 上的 Lwt.async 和 Lwt_main.run 有什么区别?
What are the differences between Lwt.async and Lwt_main.run on OCaml/Node.JS?
我正在试验 js_of_ocaml
和 node.js。如您所知,node.js 在不引入显式线程的情况下大量使用回调来实现异步请求。
在 OCaml 中,我们有一个非常好的线程库 Lwt,它带有一个非常有用的语法扩展。我编写了一个绑定到某个节点库(AWS S3 客户端)的原型,并添加了一个 lwt-ish 层来隐藏回调。
open Lwt.Infix
open Printf
open Js
let require_module s =
Js.Unsafe.fun_call
(Js.Unsafe.js_expr "require")
[|Js.Unsafe.inject (Js.string s)|]
let _js_aws = require_module "aws-sdk"
let array_to_list a =
let ax = ref [] in
begin
for i = 0 to a##.length - 1 do
Optdef.iter (array_get a i) (fun x -> ax := x :: !ax)
done;
!ax
end
class type error = object
end
class type bucket = object
method _Name : js_string t readonly_prop
method _CreationDate : date t readonly_prop
end
class type listBucketsData = object
method _Buckets : (bucket t) js_array t readonly_prop
end
class type s3 = object
method listBuckets :
(error -> listBucketsData t -> unit) callback -> unit meth
end
let createClient : unit -> s3 t = fun () ->
let constr_s3 = _js_aws##.S3 in
new%js constr_s3 ()
module S3 : sig
type t
val create : unit -> t
val list_buckets : t -> (string * string) list Lwt.t
end = struct
type t = s3 Js.t
let create () =
createClient ()
let list_buckets client =
let cell_of_bucket_data data =
((to_string data##._Name),
(to_string data##._CreationDate##toString))
in
let mvar = Lwt_mvar.create_empty () in
let callback error buckets =
let p () =
if true then
Lwt_mvar.put mvar
(`Ok(List.map cell_of_bucket_data @@ array_to_list buckets##._Buckets))
else
Lwt_mvar.put mvar (`Error("Ups"))
in
Lwt.async p
in
begin
client##listBuckets (wrap_callback callback);
Lwt.bind
(Lwt_mvar.take mvar)
(function
| `Ok(whatever) -> Lwt.return whatever
| `Error(mesg) -> Lwt.fail_with mesg)
end
end
let () =
let s3 = S3.create() in
let dump lst =
Lwt_list.iter_s
(fun (name, creation_date) ->
printf "%32s\t%s\n" name creation_date;
Lwt.return_unit)
lst
in
let t () =
S3.list_buckets s3
>>= dump
in
begin
Lwt.async t
end
由于 node.js 没有绑定到 Lwt_main
,我不得不 运行 我的代码 Lwt.async
。 运行使用 Lwt.async
而不是 Lwt_main.run
的代码有什么区别——后者在 node.js 中不存在?是否保证程序会等到异步线程完成后再退出,或者这是我的代码的一种幸运但随机的行为?
Lwt_main.run
函数递归地轮询它监督其执行的线程。在每次迭代中,如果该线程仍处于 运行ning 状态,则调度程序将使用一个引擎(来自 Lwt_engine
)通过选择或同步事件来执行等待 I/Os 的线程。
在 Node.JS 中转换它的自然方法是使用 process.nextTick
方法,它依赖于 Node.JS 自己的调度程序。在这种情况下实现 Lwt_main.run
函数可以很简单:
let next_tick (callback : unit -> unit) =
Js.Unsafe.(fun_call
(js_expr "process.nextTick")
[| inject (Js.wrap_callback callback) |])
let rec run t =
Lwt.wakeup_paused ();
match Lwt.poll t with
| Some x -> x
| None -> next_tick (fun () -> run t)
此函数仅 运行 个 unit Lwt.t
类型的线程,但这是程序的主要情况。可以使用 Lwt_mvar.t
进行通信来计算任意值。
也可以扩展此示例以支持所有类型的挂钩,就像在原始 Lwt_main.run
实现中一样。
我正在试验 js_of_ocaml
和 node.js。如您所知,node.js 在不引入显式线程的情况下大量使用回调来实现异步请求。
在 OCaml 中,我们有一个非常好的线程库 Lwt,它带有一个非常有用的语法扩展。我编写了一个绑定到某个节点库(AWS S3 客户端)的原型,并添加了一个 lwt-ish 层来隐藏回调。
open Lwt.Infix
open Printf
open Js
let require_module s =
Js.Unsafe.fun_call
(Js.Unsafe.js_expr "require")
[|Js.Unsafe.inject (Js.string s)|]
let _js_aws = require_module "aws-sdk"
let array_to_list a =
let ax = ref [] in
begin
for i = 0 to a##.length - 1 do
Optdef.iter (array_get a i) (fun x -> ax := x :: !ax)
done;
!ax
end
class type error = object
end
class type bucket = object
method _Name : js_string t readonly_prop
method _CreationDate : date t readonly_prop
end
class type listBucketsData = object
method _Buckets : (bucket t) js_array t readonly_prop
end
class type s3 = object
method listBuckets :
(error -> listBucketsData t -> unit) callback -> unit meth
end
let createClient : unit -> s3 t = fun () ->
let constr_s3 = _js_aws##.S3 in
new%js constr_s3 ()
module S3 : sig
type t
val create : unit -> t
val list_buckets : t -> (string * string) list Lwt.t
end = struct
type t = s3 Js.t
let create () =
createClient ()
let list_buckets client =
let cell_of_bucket_data data =
((to_string data##._Name),
(to_string data##._CreationDate##toString))
in
let mvar = Lwt_mvar.create_empty () in
let callback error buckets =
let p () =
if true then
Lwt_mvar.put mvar
(`Ok(List.map cell_of_bucket_data @@ array_to_list buckets##._Buckets))
else
Lwt_mvar.put mvar (`Error("Ups"))
in
Lwt.async p
in
begin
client##listBuckets (wrap_callback callback);
Lwt.bind
(Lwt_mvar.take mvar)
(function
| `Ok(whatever) -> Lwt.return whatever
| `Error(mesg) -> Lwt.fail_with mesg)
end
end
let () =
let s3 = S3.create() in
let dump lst =
Lwt_list.iter_s
(fun (name, creation_date) ->
printf "%32s\t%s\n" name creation_date;
Lwt.return_unit)
lst
in
let t () =
S3.list_buckets s3
>>= dump
in
begin
Lwt.async t
end
由于 node.js 没有绑定到 Lwt_main
,我不得不 运行 我的代码 Lwt.async
。 运行使用 Lwt.async
而不是 Lwt_main.run
的代码有什么区别——后者在 node.js 中不存在?是否保证程序会等到异步线程完成后再退出,或者这是我的代码的一种幸运但随机的行为?
Lwt_main.run
函数递归地轮询它监督其执行的线程。在每次迭代中,如果该线程仍处于 运行ning 状态,则调度程序将使用一个引擎(来自 Lwt_engine
)通过选择或同步事件来执行等待 I/Os 的线程。
在 Node.JS 中转换它的自然方法是使用 process.nextTick
方法,它依赖于 Node.JS 自己的调度程序。在这种情况下实现 Lwt_main.run
函数可以很简单:
let next_tick (callback : unit -> unit) =
Js.Unsafe.(fun_call
(js_expr "process.nextTick")
[| inject (Js.wrap_callback callback) |])
let rec run t =
Lwt.wakeup_paused ();
match Lwt.poll t with
| Some x -> x
| None -> next_tick (fun () -> run t)
此函数仅 运行 个 unit Lwt.t
类型的线程,但这是程序的主要情况。可以使用 Lwt_mvar.t
进行通信来计算任意值。
也可以扩展此示例以支持所有类型的挂钩,就像在原始 Lwt_main.run
实现中一样。