如何为cats.effect.Console添加超时机制?
问题分析与解决
原因
没错,问题根源就是Console[IO].readLine是不可取消的I/O操作。在Cats Effect中,timeoutTo的逻辑是:当原任务超时后,会立即调度执行备选任务,但如果原任务不可取消,它会继续在后台运行,直到自身完成(也就是用户输入内容后),才会最终返回备选值"Timeout!"。
解决方法
要解决这个问题,需要实现一个可取消的控制台读取操作,让超时触发时能中断原读取任务。以下是具体实现:
1. 实现可取消的readLine
import cats.effect.{IO, Async} import scala.io.StdIn def cancellableReadLine: IO[String] = Async[IO].async { callback => // 启动单独线程执行控制台读取 val readThread = new Thread(() => { try { val input = StdIn.readLine() callback(Right(input)) } catch { case _: InterruptedException => // 线程被中断时,返回取消相关的错误 callback(Left(new RuntimeException("Console read cancelled"))) } }) readThread.start() // 返回取消逻辑:中断读取线程 Some(IO(readThread.interrupt())) }
2. 结合超时逻辑
用上面的可取消读取替换原有的Console[IO].readLine,再搭配timeoutTo或者race即可:
import scala.concurrent.duration._ // 方式一:用timeoutTo def withTimeout: IO[String] = cancellableReadLine.timeoutTo(3.seconds, IO.pure("Timeout!")) // 方式二:用race(效果等价) def withTimeout: IO[String] = IO.race(IO.sleep(3.seconds).as("Timeout!"), cancellableReadLine).map(_.merge)
原理说明
手动实现的cancellableReadLine通过Async[IO].async暴露了取消逻辑:当任务被取消时(比如超时触发),会中断执行读取的线程,让原读取操作立刻终止,这样超时逻辑就能及时返回"Timeout!",无需等待用户输入。
内容的提问来源于stack exchange,提问作者Max Smirnov
相关产品推荐
相关产品推荐

