node.js API 到 ClojureScript 的惯用转换
Idiomatic conversion of node.js APIs to ClojureScript
我正在编写一个 Electron 应用程序,在这个应用程序中我需要与一些 Node.js API 进行交互 - 读取文件、获取目录条目、监听事件。
当然,我可以像编写 JavaScript 一样编写 ClojureScript,但我想知道什么是 ClojureScripts 采用回调样式 APIs、流、EventEmitter 以及我如何围绕 node.js API 编写包装器,在 ClojureScript 中看起来不那么陌生。
具体来说:
- 如何编写一个 API 包装回调样式 node.js API。 (比如说,
fs.readdir
)
- 如何与类似 EventEmitter 的 APIs 交互?
- (可能接近第 2 页)如何使用 node.js 流 API?
根据我的经验,处理这些问题的最简单方法是使用 core.async。
回调样式
例如读取一个目录:
(def fs (js/require "fs"))
(defn read-dir [path]
(let [out (async/chan)]
(.readdir fs path
(fn [err files]
(async/put! (if err err files))
(async/close! out)))
out))
我将结果传递给通道,即使该结果是错误的。这样,调用者就可以通过doing来处理错误了。例如:
(let [res (<! (read-dir "."))]
(if (instance? js/Error res)
(throw res)
(do-something res))
在我自己的项目中,我使用 cljs-asynchronize,它允许您将 NodeJS 回调样式函数转换为 core.async 兼容函数。例如,这与第一个示例相同:
(defn read-dir [path]
(asynchronize
(.readdir fs path ...)))
最后,对于通过通道处理错误的更好方法,我个人认为 "Error Handling with Clojure Async" 非常有用。因此,您可以像这样编写上面的错误处理代码:
(try
(let [res (<? (read-dir "."))]
(do-something res))
(catch js/Error e
(handle-error e))
流
流 API 更简单:
(defn create-read-stream [path]
(let [out (async/chan)
stream (.createReadStream fs path)]
(.on stream "close" #(async/close! out))
(.on stream "data" #(async/put! out %))
out))
我想你的问题的答案是 "It depends."。
始终考虑所有选项。仅仅因为 core.async
在一种设置中有意义并不能使它成为所有地方的最佳选择。
如果只调用一次并且不需要与其他任何东西协调(例如 fs.readdir
),则可以使用普通回调。
core.async
非常适合像流这样的事情,在这种情况下,您会获得要累积到最终结果的特定事件链(例如 "data","data","data","close","end"
)。
承诺也是另一种选择。
您需要的协调越多,core.async
就越有意义。如果您不需要,请考虑替代方案。
我正在编写一个 Electron 应用程序,在这个应用程序中我需要与一些 Node.js API 进行交互 - 读取文件、获取目录条目、监听事件。
当然,我可以像编写 JavaScript 一样编写 ClojureScript,但我想知道什么是 ClojureScripts 采用回调样式 APIs、流、EventEmitter 以及我如何围绕 node.js API 编写包装器,在 ClojureScript 中看起来不那么陌生。
具体来说:
- 如何编写一个 API 包装回调样式 node.js API。 (比如说,
fs.readdir
) - 如何与类似 EventEmitter 的 APIs 交互?
- (可能接近第 2 页)如何使用 node.js 流 API?
根据我的经验,处理这些问题的最简单方法是使用 core.async。
回调样式
例如读取一个目录:
(def fs (js/require "fs"))
(defn read-dir [path]
(let [out (async/chan)]
(.readdir fs path
(fn [err files]
(async/put! (if err err files))
(async/close! out)))
out))
我将结果传递给通道,即使该结果是错误的。这样,调用者就可以通过doing来处理错误了。例如:
(let [res (<! (read-dir "."))]
(if (instance? js/Error res)
(throw res)
(do-something res))
在我自己的项目中,我使用 cljs-asynchronize,它允许您将 NodeJS 回调样式函数转换为 core.async 兼容函数。例如,这与第一个示例相同:
(defn read-dir [path]
(asynchronize
(.readdir fs path ...)))
最后,对于通过通道处理错误的更好方法,我个人认为 "Error Handling with Clojure Async" 非常有用。因此,您可以像这样编写上面的错误处理代码:
(try
(let [res (<? (read-dir "."))]
(do-something res))
(catch js/Error e
(handle-error e))
流
流 API 更简单:
(defn create-read-stream [path]
(let [out (async/chan)
stream (.createReadStream fs path)]
(.on stream "close" #(async/close! out))
(.on stream "data" #(async/put! out %))
out))
我想你的问题的答案是 "It depends."。
始终考虑所有选项。仅仅因为 core.async
在一种设置中有意义并不能使它成为所有地方的最佳选择。
如果只调用一次并且不需要与其他任何东西协调(例如 fs.readdir
),则可以使用普通回调。
core.async
非常适合像流这样的事情,在这种情况下,您会获得要累积到最终结果的特定事件链(例如 "data","data","data","close","end"
)。
承诺也是另一种选择。
您需要的协调越多,core.async
就越有意义。如果您不需要,请考虑替代方案。