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 中看起来不那么陌生。

具体来说:

  1. 如何编写一个 API 包装回调样式 node.js API。 (比如说,fs.readdir
  2. 如何与类似 EventEmitter 的 APIs 交互?
  3. (可能接近第 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 就越有意义。如果您不需要,请考虑替代方案。