阻塞直到 DOM 事件发生 core.async

Block until a DOM event occurs with core.async

我有一个 Clojurescript 项目,我需要阻止整个线程执行,直到发生 DOM 事件。

在这种情况下,事件是 DOMContentLoaded,当初始 HTML 文档已完全加载和解析时触发。但它可以扩展到任何 DOM(或非 DOM)事件。

由于我是 Clojurescript 和异步的新手,所以我不确定如何解决这个问题。我的第一个猜测是使用 core.async 库。经过一些文档抓取后,我有了这个功能:

(defn wait-dom-loading
  []
  (let [c (async/chan)]
 {1} (.addEventListener js/document "DOMContentLoaded" (fn [] (async/go (async/>! c true))))
 {2} (async/go (async/<! c))))

我的理解是 {2} chan c 获取 并停放直到 {1} 中的听众评估函数和 puts chan c.

中的一个值

因为我几乎不了解如何对异步代码进行单元测试(除了将其放入(异步完成)表达式并在完成时调用 done)我无法验证我所做的是否正确。我试过这个片段:

(do
  (wait-dom-loading)
  (-> (dommy/sel1 :p)
      (dommy/set-text! "Loaded !")))

在 html 页面内有一个 p 块,并注意到控制台抱怨 js 代码试图操纵一个 DOM 对象'还存在。这证实了我所做的并没有按计划工作。

  1. 这个例子中似乎有什么问题?
  2. 这是矫枉过正吗?我可以用更小的解决方案甚至 gasp 内置函数来解决这个问题吗?
  3. 将我的脚本放在我的 html 页面底部是不是很糟糕的做法?

因为这是我关于堆栈溢出的第一个问题,我希望它写得足够​​好。

我会这样做:

(ns domevent.core
    (:require [cljs.core.async :as async :refer [chan]])
    (:require-macros [cljs.core.async.macros :refer [go]]))

(enable-console-print!)    
(def ch (chan))

(go
    (pr "i am waiting")
    (pr (<! ch))
    (dommy/set-text! (dommy/sel1 :p) "Loaded !"))

(.addEventListener js/document "DOMContentLoaded" (fn [] (go (>! ch "hello"))))

或者更简单地说:

(let [ch (chan)]
    (go
        (pr "i am waiting")
        (<! ch)
        (dommy/set-text! (dommy/sel1 :p) "Loaded !"))

    (.addEventListener js/document "DOMContentLoaded" #(close! ch)))
)

这里的重点是reader和writer共享的ch。当 (<! ch) 发生时,ch 中还没有任何内容,因此该线程被停放(即停止并等待任何内容出现在 ch 中)。同时,DOMContentLoaded 发生,处理程序写入通道。那么之前的话题继续。