You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在自定义方法中保留ZIO响应?调用fill方法无反应求助

问题描述

我写了以下方法来从OpenDota API获取联赛数据并写入文件,但调用后毫无反应:

import ClientServer.*
import zio.http.{Client, *}
import zio.json.*
import zio.http.model.Method
import zio.{ExitCode, URIO, ZIO}
import sttp.capabilities.*
import sttp.client3.Request
import zio.*
import zio.http.model.Headers.Header
import zio.http.model.Version.Http_1_0
import zio.stream.*
import java.net.InetAddress
import sttp.model.sse.ServerSentEvent
import sttp.client3._

object fillFileWithLeagues:
  def fill = for {
    openDotaResponse <- Client.request("https://api.opendota.com/api/leagues")
    bodyOfResponse <- openDotaResponse.body.asString
    listOfLeagues <- ZIO.fromEither(bodyOfResponse.fromJson[List[League]].left.map(error => new Exception(error)))
    save = FileStorage.saveToFile(listOfLeagues.toJson) //Ok
  }yield ()
  println("Im here fillFileWithLeagues.fill ")

之后我又写了一个方法尝试读取文件,不存在就调用上面的方法生成文件,但依然没反应:

def readFromFileV8(path: Path = Path("src", "main", "resources", "data.json")): ZIO[Any, Throwable, String] =
  val zioStr = (for bool <- Files.isReadable(path) yield bool).flatMap(bool =>
  if (bool) Files.readAllLines(path, Charset.Standard.utf8).map(_.head)
  else {
    fillFileWithLeagues.fill
    wait(10000)
    println("im here readFromFileV8")
    readFromFileV8()})
  zioStr

我期望从Client.request("https://api.opendota.com/api/leagues")获取数据并生成data.json文件,但完全没效果。请问需要改用sttp或其他工具吗?


问题分析与解决

核心问题:ZIO任务未被正确执行

你的代码问题和用不用sttp无关,主要是ZIO的惰性执行特性没掌握好,以下是具体修正点:

  1. fill方法里的保存任务未执行
    ZIO是惰性的,仅定义任务(save = ...)不会触发执行,必须用<-将任务加入执行流程。另外,外部的println会在方法定义时就执行,而非任务完成后,要包装成ZIO任务放入流程中:

    def fill = for {
      openDotaResponse <- Client.request("https://api.opendota.com/api/leagues")
      bodyOfResponse <- openDotaResponse.body.asString
      listOfLeagues <- ZIO.fromEither(bodyOfResponse.fromJson[List[League]].left.map(error => new Exception(error)))
      _ <- FileStorage.saveToFile(listOfLeagues.toJson) // 用<-执行保存任务
      _ <- ZIO.succeed(println("Im here fillFileWithLeagues.fill ")) // 包装成ZIO任务在流程内执行
    } yield ()
    
  2. readFromFileV8里的执行逻辑错误

    • fillFileWithLeagues.fill仅引用任务未执行,需要用<-或*>(顺序执行操作符)加入流程
    • 不能用Java的wait阻塞线程,要改用ZIO的非阻塞睡眠ZIO.sleep
    • 无需递归轮询,确保fill执行完成后直接读取即可:
    def readFromFileV8(path: Path = Path("src", "main", "resources", "data.json")): ZIO[Any, Throwable, String] =
      Files.isReadable(path).flatMap { bool =>
        if (bool) {
          Files.readAllLines(path, Charset.Standard.utf8).map(_.head)
        } else {
          fillFileWithLeagues.fill *> // 先执行fill,完成后读取文件
            Files.readAllLines(path, Charset.Standard.utf8).map(_.head)
        }
      }
    
  3. ZIO任务需要在Runtime中启动
    所有ZIO任务必须通过Runtime执行,比如在main方法中启动:

    object Main extends ZIOAppDefault {
      override def run = readFromFileV8().exitCode
    }
    

    单纯调用readFromFileV8()只会返回任务描述,不会实际执行。

是否需要改用sttp?

不需要,zio-http的Client完全能完成该请求任务。当前问题是ZIO使用逻辑错误,而非HTTP客户端的问题,修正上述逻辑后现有代码即可正常工作。


内容的提问来源于stack exchange,提问作者BlackFriday

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.09 17:45:34