使用Cats Effect Ref实现缓存:为何内部Ref未按预期设置?
Cats Effect Ref 缓存未按预期设置值
尝试用函数式方式借助Cats Effect的Ref monad实现缓存,但执行后发现Ref内部值未被正确设置,设置操作完成后读取依然是None。
问题代码
import cats.effect.kernel.Ref import cats.effect.{IO, IOApp} object SomeMain extends IOApp.Simple { val cache: IO[Ref[IO, Option[String]]] = Ref.of[IO, Option[String]](None) override def run: IO[Unit] = { val checkValueBeforeSet = cache.flatMap(ref => ref.get.flatMap { case Some(v) => IO(println(v)) case None => IO(println("as expected no value yet")) }) val doSetAction = cache.flatMap(ref => ref.set(Some("abc"))).map(_ => println("set action done")) val checkValueAfterSet = cache.flatMap(ref => ref.get.flatMap { case Some(v) => IO(println(v)) case None => IO(println("unexpected still no value set!")) }) for { _ <- checkValueBeforeSet _ <- doSetAction _ <- checkValueAfterSet } yield IO() } }
输出结果
as expected no value yet set action done unexpected still no value set!
问题根源
问题出在cache的定义上:val cache: IO[Ref[IO, Option[String]]] = Ref.of(None)。这里的cache是一个IO操作,每次调用cache(比如在三个检查/设置步骤中)都会执行Ref.of,创建一个全新的Ref实例:
checkValueBeforeSet用的是第一个Ref(初始值None)doSetAction用的是第二个全新的Ref,设置了值但和第一个无关checkValueAfterSet用的是第三个全新的Ref(初始值还是None)
三次操作根本没共享同一个Ref,自然读不到设置后的值。
解决方案
只创建一次Ref实例,然后在整个流程中复用它:
import cats.effect.kernel.Ref import cats.effect.{IO, IOApp} object SomeMain extends IOApp.Simple { override def run: IO[Unit] = { // 仅初始化一次Ref实例 Ref.of[IO, Option[String]](None).flatMap { cache => val checkValueBeforeSet = cache.get.flatMap { case Some(v) => IO(println(v)) case None => IO(println("as expected no value yet")) } val doSetAction = cache.set(Some("abc")).map(_ => println("set action done")) val checkValueAfterSet = cache.get.flatMap { case Some(v) => IO(println(v)) case None => IO(println("unexpected still no value set!")) } for { _ <- checkValueBeforeSet _ <- doSetAction _ <- checkValueAfterSet } yield () } } }
修改后的输出会变成:
as expected no value yet set action done abc
所有操作共享同一个Ref实例,设置和读取的是同一个状态,就能得到预期结果。
内容的提问来源于stack exchange,提问作者jakstack
相关产品推荐
相关产品推荐

