gpt4 book ai didi

javascript - 如何在不使用 clojurescript 中的事件循环的情况下运行长时间的计算?

转载 作者:行者123 更新时间:2023-11-29 14:50:40 25 4
gpt4 key购买 nike

一个简单(但慢)的函数

我想利用 clojurescript 在浏览器中进行一些声明式、函数式编程——例如,能够进行惰性计算,例如:

(def special-nums 
(->> (iterate inc 1)
(filter #(= (mod % 100) 0))
(filter #(= (mod % 7001) 0))))

(time (doall (take 1 special-nums)))

(请注意,这个特定的计算对我来说一点也不有趣——它只是一些代码的示例,即使返回第一个结果也需要未知的时间)

有什么方法可以分块限制 CPU 时间?

像这样的代码对于 clojure 来说很自然,但它不适合在浏览器环境中调用,在浏览器环境中它可能会破坏事件循环并使网页无响应。懒惰无济于事,因为即使是第一个结果也可能需要很长时间才能返回(在我的机器上为 1500 毫秒)。

在“普通”javascript 中,使用命令式循环,我会将范围分块并使用 setTimeout 异步返回结果,从而限制我愿意在任何给定 block 中完成的工作。 (我什至可以明确引用时钟 - 例如“继续工作直到 20 毫秒过去,然后停止并安排另一个 block 。)

有没有在 clojurescript 中完成此操作的好方法?显然,通过 js 互操作,一切皆有可能,但如果我与系统作斗争太过分,cljs 的值(value)就会受到限制。

如果有任何建议/技巧,我将不胜感激。

注意事项

我知道网络 worker ,我知道将计算交给另一个执行线程总是可能的——但对于这个问题,我想关注在单个 JS 事件循环中工作的方法。

谢谢!

最佳答案

激发这个答案的技术是 trampoline - 返回答案的函数,或返回答案或函数的另一个函数.....(在此处插入递归英文文本)。

我们实际上不能使用clojure.core/trampoline在这里是因为它会占用 JS 消息循环。相反,我们可以使用类似下面的东西,它会在 js/setTimeout 中“反弹”。当 f 返回一个函数时,我们从 setTimeout 调用它。当 f 返回任何其他内容时,我们调用 (continuation result)

(defn continue-trampoline [continuation f & args]
(let [result-or-fn (apply f args)
is-answer (not (fn? result-or-fn))]
(if is-answer (continuation result-or-fn)
(do (js/setTimeout #(continue-trampoline continuation result-or-fn) 1)
nil))))

这让我们可以将问题分成更小的部分,可以在更短的时间内单独解决。

以您的特殊数字为例,您可以这样分解它:

(defn calculate-special-nums [n continuation]
(letfn [(accumulate-special-nums [accumulator partitions]
(if (empty? partitions) accumulator
(let [part (first partitions)
remaining-n (- n (count accumulator))
acc (->> part
(filter #(= (mod % 100) 0))
(filter #(= (mod % 7001) 0))
(take remaining-n)
(into accumulator))
is-complete (== n (count acc))]
(if is-complete acc
#(accumulate-special-nums acc (rest partitions))))))]
(let [nums (iterate inc 1)
partitions (partition 1000 nums)]
(continue-trampoline continuation
#(accumulate-special-nums [] partitions)))))

所以这段代码会计算 10 个特殊数字,并在计算完所有 10 个时提醒他们,而不会使消息循环的其他用户挨饿。

(calculate-special-nums 10 #(js/alert %))

该技术可能会扩展到计算经过的毫秒数。对于您的示例,我可以想象使用 partition-by 而不是 partition。创建一个在一段时间后返回 true 的函数。例如(partition-by has-the-time-elapsed?nums) 而不是 (partition 1000 nums)

其他问题

正如您所说,即使在“普通”javascript 中,您也必须分解问题 - 对于昂贵的计算,clojurescript 可能是也可能不是异常(exception)。使用纯函数范式编程的一大好处是每个部分都是独立可测试的。对于每个不同的输入,输出总是相同的。希望 clojurescript 甚至可以使分区问题变得更容易。

关于javascript - 如何在不使用 clojurescript 中的事件循环的情况下运行长时间的计算?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/26196914/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com