【问题标题】:ClojureScript: How to access (not just print) the result of an HTTP GET requestClojureScript:如何访问(不仅仅是打印)HTTP GET 请求的结果
【发布时间】:2021-06-05 10:44:39
【问题描述】:

我正在尝试使用 Clojure(服务器)和 ClojureScript(客户端)构建一个简单的客户端-服务器系统。

服务器端正常(浏览器控制台上一切都是绿色的,没有CORS问题等);响应是纯文本(“真”或“假”)。在客户端,我基本上使用official cljs-http website中包含的功能

(defn get-request [str1 str2]
(go (let [response (<! (http/get "http://localhost:3000"
                                 {:query-params {"str1" str1
                                                 "str2" str2}}))]
  (prn response))))

在 REPL 上运行这个函数表明一切都很好,并且类似地图的东西从异步通道中出来

cljs.user=> (b/get-request "foo" "bar")
#object[cljs.core.async.impl.channels.ManyToManyChannel]         ;; the result
cljs.user=> {:status 200, :success true, :body "false", [...]}   ;; output of `prn`

我的问题是:我怎样才能真正从ManyToManyChannel“对象”中获得响应的正文?

  • 为了测试,将函数的最后一个表达式更改为(prn (:body response)) 打印 "false" 但仍然返回ManyToManyChannel
  • 将函数的最后一个表达式更改为 (:body response) 出乎意料地返回相同的结果而不是“false”。

我该如何继续?

【问题讨论】:

  • 您的函数返回的是 go 宏的结果,而不是其中的内容。
  • @JaredSmith:我知道——结果里面是:body "false" 条目。问题是如何获取该条目?
  • 调用代码中的consuming the returned channel。没有办法将异步操作变成同步操作,这就是重点。
  • @JaredSmith:我试过了,但take! 仅适用于 Clojure,我的客户端在 ClojureScript 上运行。我忽略了什么吗?谢谢
  • 啊,错误的链接我的错。你想要

标签: clojurescript


【解决方案1】:

您无法以同步方式访问async 操作的结果(例如go)。所以获得实际响应的唯一方法是在异步回调中。

REPL 中获取结果有点棘手。您可以将它存储在原子中,并在操作完成后从原子中访问它。

(def result-ref (atom nil))

(defn into-ref [chan]
  (go (reset! result-ref (<! chan))))

(into-ref
  (http/get "http://localhost:3000"
    {:query-params
     {"str1" str1
      "str2" str2}}))

@result-ref

当然,您只能在操作实际完成后才能访问结果。 REPL 对于异步操作有点棘手,但这个技巧可能会有所帮助。

【讨论】:

  • 与仅将响应值放回通道相比,轮询 ref 非常容易出错。
  • 这个例子演示了如何从一个通道中获取一个值,以便在 REPL 中与它进行交互。我没有说任何关于反复轮询或类似的事情。
  • 您的方法有效(使用原子来保存结果),但我注意到一些时间问题(在写作之前阅读)-也许@Jared Smith 是对的......无论如何,我会尝试两种方法跨度>
  • 用于在 REPL 中进行探索。永远不要不要在常规代码中这样做!
  • @ThomasHeller 公平,我推断。但我认为不习惯处理异步性的人会认为这意味着你在实际代码中就是这样做的。我将这个问题解释为不仅仅是关于如何与 REPL 的频道互动。
【解决方案2】:

今天是星期六早上,我很懒,所以我没有运行这个,但这应该可以:

(defn get-request [str1 str2]
  (go (<! (http/get "http://localhost:3000"
                    {:query-params {"str1" str1
                                    "str2" str2}}))))


(defn read-response [response-chan]
  (go (let [resp (<! response-chan)]
        (prn resp))))  ; <- the only place where you can "touch" resp!

(read-response (get-request "a" "b"))

go 块返回一个通道,该通道将接收您在请求响应中放入其中的结果(原始 go 块的返回形式)。

读取函数获取一个通道并从中读取,最后一种形式只是调用第二个函数,结果调用第一个。

请记住,渠道是一流的构造,您可以传递它们、与多个消费者共享它们等等。

【讨论】:

  • 抱歉,我想查询响应,而不是打印它。假设resp 包含所有数据(在从某个通道中提取一次或两次之后),最终我想运行(:body resp) 之类的东西,这样我就有了字符串“true”或“false”比较。换句话说:我想 work 处理响应,而不仅仅是使用 prn 显示它
  • @JorgeD ...我刚刚为您提供了这样做的秘诀。你不必只是打印它,你可以用它做任何你想做的事情。我的答案与您问题中的代码之间的区别在于,我向您展示了如何在任意函数中提取响应数据,而不仅仅是在您发起 HTTP 请求本身的 go 块中。您似乎想要但又不能做的是将来自 HTTP 请求的响应转换为 同步 结果(即返回 (:body resp))。一旦你去异步,everything 都是异步的,因为返回是立即的,但值还没有准备好。
  • 我意识到来自 Java 背景有点奇怪,但 Javascript 的第一条规则是不要阻塞。 Javascript 的第二条规则是你不要阻塞。因为只有一个线程,如果你阻塞,那么你的 UI 完全没有响应。做你想做的事情的唯一方法是阻塞一个线程并等待,只有一个线程要阻塞,而 Clojurescript 编译器(明智地,正确地)不会让你这样做,这就是阻塞的原因Clojurescript 中缺少 core.async 中的变体(&lt;!!take! 等)。
  • 首先,非常感谢您的帮助。这是一个棘手的情况 * 我在 REPL 上尝试了你的建议,但它没有奏效 - 可能是因为 REPL 和异步很难处理,正如@thomas-heller 所写...... * ...因此我尝试了他的解决方案(使用atom) 并且它可以工作,但我有一些时间问题——你可能是对的,因为这种方法容易出错程序
猜你喜欢
  • 2019-05-16
  • 1970-01-01
  • 2013-03-19
  • 1970-01-01
  • 1970-01-01
  • 2019-09-15
  • 2018-12-15
  • 1970-01-01
  • 2012-10-21
相关资源
最近更新 更多