如何在继续 ClojureScript 中的下一次调用之前等待服务器响应?

How to wait for server response before proceeding to next call in ClojureScript?

(def tables (atom nil)) 

(defn validateDatasource [datasource]
        (get-tables tables)
        (js/console.log @tables)
    )

(defn get-tables [tables]
    (ajax/GET "/tables"
    {:headers {"Accept" "application/transit+json"}
    :handler #(reset! tables (vec %))}
    ))

这里我在单击按钮时调用 validateDatasource 并且 第一次单击时它打印 null.. 但是一段时间后,如果我 再次单击它会在控制台中打印表格映射。

因此我想知道如何在继续打印之前等待服务器响应?

ajax/GET 是一个异步调用。这意味着它立即 returns,无需等待服务器的响应。

您可能想要使用 core.async 或者,如果它适合您,流形库。有个port to CLJS.

对你的问题最直接的回答是:你不能在浏览器端等待 javascript 因为 javascript 是严格异步和单线程的。

您的选择是 (1) 编写回调代码,(2) 编写承诺代码,(3) 使用 core.async。

(1) 回调。 不要在服务器上尝试 "wait" 到 return(你不能这样做),而是将代码作为回调:

(defn get-tables [result-atom next]
  (ajax/GET "/tables"
            {:headers {"Accept" "application/transit+json"}
             :handler (fn [response]
                        (reset! result-atom (vec response))
                        (next result-atom))})) ; This is the key line

(defn validate-datasource [datasource]
  (js/console.log datasource))

(defn get-and-validate-tables [result-atom]
  (get-tables result-atom validate-datasource))

请注意,鉴于您将原子作为参数传递(并隐藏顶级定义),最好完全删除它并让 handler 将响应直接传递给 next 没有弄乱原子。

(2) Promises. 如果你只做一件事,回调很好,但当你试图将多个链接在一起时,它会变得复杂。您可以使用 promesa 库编写使回调更易于处理的承诺代码。

(defn get-tables
  []
  (promesa/promise
    (fn [resolve reject]
      (ajax/GET "/tables"
                {:headers {"Accept" "application/transit+json"}
                 :handler resolve
                 :error-handler reject}))))

;; Note, this function returns a promise, so you can call then and catch on it
(get-tables-and-validate []
  (-> (get-tables)
      (promesa/then (fn [response]
                      (validate-datasource response)))
      (promesa/catch (fn [error]
                       (js/console.log error)))))

这很好,因为您可以使用对 then 的调用将 promise-returning 调用链接在一起。错误处理也很有效。

3。 Core.async. 您也可以尝试使用 core.async 库,但我会警告您,它引入了很多您可能不需要的复杂性和代码。处理异常也很尴尬,我遇到过极端情况和错误,但没有做任何复杂的事情。在我看来,上面的 promise 代码更健壮,更容易理解。

最后,如果您真的想要原子验证,请注意您可以在原子上使用 set-validator!

(set-validator! tables (fn [atm] (validate-datasource atm)))

现在,每当您更新 tables 原子时,验证器都会自动 运行。