如何在自定义方法中保留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的惰性执行特性没掌握好,以下是具体修正点:
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 ()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) } }ZIO任务需要在Runtime中启动
所有ZIO任务必须通过Runtime执行,比如在main方法中启动:object Main extends ZIOAppDefault { override def run = readFromFileV8().exitCode }单纯调用
readFromFileV8()只会返回任务描述,不会实际执行。
是否需要改用sttp?
不需要,zio-http的Client完全能完成该请求任务。当前问题是ZIO使用逻辑错误,而非HTTP客户端的问题,修正上述逻辑后现有代码即可正常工作。
内容的提问来源于stack exchange,提问作者BlackFriday
相关产品推荐
相关产品推荐

