0

我正在使用 rxscala 并发现了一个非常微妙的问题,我的代码简化为以下内容:

import rx.lang.scala.Observable
import rx.lang.scala.subjects.PublishSubject

object SubtleBug extends App {

  case class Projects(projects: List[Project] = Nil)
  case class Project(name: String, docs: List[Doc] = Nil)
  case class Doc(path: String, baseContent: String)

  sealed trait ServerEvent
  case class ProjectNames(projects: Seq[String]) extends ServerEvent
  case class NewDocument(projectName: String, path: String, version: Int, content: String) extends ServerEvent

  val receivedEvents = PublishSubject[ServerEvent]

  new Thread(new Runnable {
    override def run(): Unit = {
      val events = Seq(
        new ProjectNames(Seq("p1", "p2")),
        NewDocument("p1", "/aaa", 1, "my-content"),
        NewDocument("p1", "/bbb", 1, "my-content")
      )
      events.foreach { event =>
        receivedEvents.onNext(event)
        Thread.sleep(200)
      }
    }
  }).start()

  lazy val projects: Observable[Option[Projects]] = receivedEvents.scan(Option.empty[Projects]) {
    case (_, ProjectNames(names)) => {
      Some(Projects(names.map(name => Project(name)).toList))
    }
    case (Some(ps), NewDocument(projectName, docPath, version, content)) => {
      val doc = Doc(docPath, content)
      val newPs = ps.copy(projects = ps.projects.map {
        case project if project.name == projectName => project.copy(docs = project.docs ::: List(doc))
        case p => p
      })
      Some(newPs)
    }
    case _ => None
  }.collect({
    case Some(p) => Some(p)
  })

  projects.foreach(ps => {
    println("### 111: " + ps.map(_.projects))
    projects.foreach(x => println("### 222: " + ps.map(_.projects))) // !!!(2)
  })

  Thread.sleep(2000)

}

关键点是!!!(2)线,它在projects自身内部。

它打印以下内容:

### 111: Some(List(Project(p1,List()), Project(p2,List())))
### 111: Some(List(Project(p1,List(Doc(/aaa,my-content))), Project(p2,List())))
### 111: Some(List(Project(p1,List(Doc(/aaa,my-content), Doc(/bbb,my-content))), Project(p2,List())))

问题是没有### 222:线!

但是,如果我更改collect部分并添加None案例:

.collect({
  case Some(p) => Some(p)
  case None => None // added case
})

它将### 222:按我的预期打印行:

### 111: None
### 111: Some(List(Project(p1,List()), Project(p2,List())))
### 111: Some(List(Project(p1,List(Doc(/aaa,my-content))), Project(p2,List())))
### 222: None
### 222: None
### 222: Some(List(Project(p1,List()), Project(p2,List())))
### 222: Some(List(Project(p1,List()), Project(p2,List())))
### 111: Some(List(Project(p1,List(Doc(/aaa,my-content), Doc(/bbb,my-content))), Project(p2,List())))
### 222: None
### 222: Some(List(Project(p1,List()), Project(p2,List())))
### 222: Some(List(Project(p1,List(Doc(/aaa,my-content))), Project(p2,List())))
### 222: Some(List(Project(p1,List(Doc(/aaa,my-content))), Project(p2,List())))

我不明白为什么。

PS:您可以在此处克隆代码:https ://github.com/freewind/rxscala-test/blob/master/src/main/scala/myrx/SubtleBug.scala

4

1 回答 1

0

您的代码中存在竞争条件。PublishSubject将在您订阅之前删除任何事件。因此,如果receivedEvents.onNext(event)在 之前运行projects.foreach,事件将被丢弃。在 中scan,如果第一个事件被删除,模式匹配将不再起作用。

你可以用ReplaySubject它来修复它。

于 2015-11-20T19:04:30.527 回答