scalamonix

Why are results ordered in this Task.parSequenceUnordered? Monix


Because Task.parSequenceUnordered "is like parSequence, except that you don’t get ordering for results or effects.", I would expect the following to return List(2, 1):

import monix.eval.Task
import monix.execution.Scheduler.Implicits.global


object TaskSequence extends App {

    val taskOne = Task {
      Thread.sleep(3000)
      println("First executed")
      1
    }
    val taskTwo = Task {
      Thread.sleep(2000)
      println("Second executed")
      2
    }
  
    val parallelUnordered = Task.parSequenceUnordered(List(taskOne, taskTwo)).runSyncUnsafe()
    println(s"Return vals for Task.parSequenceUnordered( ) were $parallelUnordered")
}

However, I get List(1, 2) as a result. Using .runAsync( ) doesn't make a difference either:

    Task.parSequenceUnordered(List(taskOne, taskTwo)).runAsync( result => result match {
      case Right(value) => println(value)
      case Left(value) => value
    })
  Thread.sleep(5000) // don't let program exit

The second Task finishes first, so I should get the 2 back first in the return List, right? Can any Monix experts weigh in? Thanks


Solution

  • It's because of the implementation of parSequenceUnordered, which builds the result list by using O(1) prepend operations.

    There's a

    private sealed abstract class State[+A] {
      def isActive: Boolean
      def enqueue[B >: A](value: B): State[B]
    }
    

    to represent the internal state machine for the task, and a couple of the State subclasses look like

    final case class Active[+A](list: List[A], remaining: Int) extends State[A] {
    
      def isActive = true
      def enqueue[B >: A](value: B): Active[B] =
        Active(value :: list, remaining - 1)
    }
    

    So because it uses value :: list to accumulate the results, they are built in reverse in terms of which result comes in first.