ClojureScript 中回调中的 clearInterval

clearInterval inside callback in ClojureScript

基本上,我想要的是在ClojureScript中实现这段代码:

var win = window.open('foo.html', 'windowName');   
var timer = setInterval(function() {   
    if(win.closed) {  
        clearInterval(timer);  
        alert('closed');  
    }  
}, 1000);

我试过这个:

(let [popup (.open js/window "foo.html" "windowName")
      interval (.setInterval
                js/window
                (fn []
                  (when (.-closed popup)
                    (do
                      ;; 'interval' is undefined at this point
                      (.clearInterval js/window interval)

                      (.alert js/window 'closed')))
                1000)]
...)

但是 CLJS 编译器警告我 interval 未定义。

有什么想法吗?

问题是您在定义绑定之前在匿名函数中访问了 interval 本地绑定(在绑定到 interval 符号之前必须先评估右侧,直到那么 interval 未定义。

您可以通过定义一个原子来存储您的间隔并从您的回调函数访问它来解决它:

(let [popup (.open js/window 'foo.html', 'windowName')
      interval (atom nil)]
  (reset! interval (.setInterval
                    js/window
                    (fn []
                      (when (.-closed popup)
                        (do
                          (.clearInterval js/window @interval)
                          (.alert js/window "Closed")))))))

我不确定是否有更优雅的方法来使用您的间隔回调方法来实现它。

另一种方法是使用直接 js 互操作:

(let [popup (.open js/window "foo.html" "windowName")]
  (js* "var interval = setInterval(function() {
          if (popup.closed) {
            clearInterval(interval);
            alert('close');
          }
        }, 500);")

    ...)

使用 atom

  • 容易
  • 并且包含在 ClojureScript 的核心功能中

然而 interval atom 可能无法清楚地表达其实际性质和意图。
最初的问题是编译器抱怨 未定义 interval。所以需要的是一个值:

  • 可以先定义
  • 已解决(已交付)稍后

对我来说这听起来像是 promise。从 ClojureScript 1.8.34 Clojure promises aren't supported yet. Because of the existence of core.async there are indications that support of Clojure's promise isn't an urgent priority in ClojureScript - especially as core.async contains promise-chan 开始。使用promise-chan,代码可以写成如下

(ns test.core
  (:require-macros [cljs.core.async.macros :refer [go]])
  (:require [cljs.core.async :refer [<! close! promise-chan put!]]))

(let [interval (promise-chan)
      fooWin (.open js/window "./foo.html", "windowName")
      checkOnFooWin 
        (fn []
          (when (.-closed fooWin) ;; when has an implicit do
            (go
              (.clearInterval js/window (<! interval)))
            (.alert js/window "Closed")))]
  (put! interval (.setInterval js/window checkOnFooWin 500))
  (close! interval))

此代码既不比 atom 版本更简单也不优雅 - 但是它确实具有

的优势
  • interval只能解决一次
  • interval 中获取的每个值将始终是最初提供的值。