迭代回调地狱(在 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 通道处理来处理每个结果。如果你确实需要一个数组,那么你应该找到另一种方法(遍历所有文档并单独加载它们)这样你就可以确定所有文件加载完成的时间。
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 通道处理来处理每个结果。如果你确实需要一个数组,那么你应该找到另一种方法(遍历所有文档并单独加载它们)这样你就可以确定所有文件加载完成的时间。