迭代回调地狱(在 ClojureScript 中)

Iterated Callback Hell (in ClojureScript)

1 有问题的 JavaScript 函数

我正在处理 JS 中一个非常有问题的函数,该函数调用回调函数迭代 次。特别是,该函数采用 yaml 字符串并为在字符串中找到的 每个 数量的 yaml 文档运行回调函数:

var yaml = require('js-yaml');

yaml.safeLoadAll(data, function (doc) {
  console.log(doc);
  });

所以在这里,如果 data 包含 2 个 yaml 文档,那么我们将在控制台中看到 2 个日志。

2 在 ClojureScript 中处理它

假设 string 未知 数量的 yaml 文档。我想使用 core.async 通道将这些文档中的每一个放入 javascript 数组中。

首先,我创建了一个函数,将每个 yaml 文档压缩到一个频道中:

(defn yaml-string->yaml-chan [string]
  (let [c (chan)]
    (go 
      (.safeLoadAll 
       yaml
       string 
      (fn [current-yaml-object] 
        (go 
          (>! c current-yaml-object)
          ;(close! c) ; cant close here or we only get one doc!
        )
      ))  
    ) c ; here we return the channel
  )
)

然后我创建了一个函数,它从频道中吸取每个 yaml 文档并将它们粘贴到一个 javascript 数组中(封装在另一个频道中)。

(defn yaml-chan->array-chan [c]
  (let [arr (js/Array.) arr-chan (chan) a (atom true)]
    (go
      (reset! a (<! c))
      (while (not-nil? @a)
    (.push arr @a)
    (reset! a (<! c))
      )
      (>! arr-chan arr)
    ) arr-chan
  )
)

然后我尝试执行结果:

(go (println <! (yaml-chan->yaml-array-chan (yaml-string->yaml-chan string)))

我得到的只是 #object[cljs.core.async.impl.channels.ManyToManyChannel] :( 我认为这是因为我从未关闭 yaml 对象的原始通道。但是我如何使用迭代回调函数执行此操作?在哪里以及如何关闭那个频道?

我不会让你的回调在每次调用时都生成一个通道。相反,我会创建一个函数来关闭您作为回调传入的通道。然后,所有文档将被推送到此频道,您可以在准备好后阅读该频道以获取文档。

我不认为 core.async 在这种情况下会有很大帮助。这在普通 javascript 中也很困难。 safeLoadAll 只要有更多文档要加载,就会触发回调。除了使用某种不稳定的超时检查之外,没有办法知道它是否完成(这不能 保证 一切都已加载,只是没有 activity已在时间阈值内发生)。

如果您不需要将所有文档收集到一个数组中,那么您可以使用 core.async 通道处理来处理每个结果。如果你确实需要一个数组,那么你应该找到另一种方法(遍历所有文档并单独加载它们)这样你就可以确定所有文件加载完成的时间。