Bạn có chắc chắn muốn xóa bài viết này không ?
Bạn có chắc chắn muốn xóa bình luận này không ?
Chú ý khi dùng for comprehension với Future trong scala
Nãy mình có trao đổi trên scala group trên facebook và nhận thấy một điểm cơ bản cực dễ nhầm khi dùng Future trong scala.
Có đoạn code dưới đây
import scala.concurrent._
import scala.concurrent.ExecutionContext.Implicits.global
import scala.concurrent.duration._
import scala.util.{Success,Failure}
import scala.collection.immutable._
object Foo extends App {
def test1 = Future {
Thread.sleep(2000)
"test1"
}
def test2 =
Future {
Thread.sleep(2000)
"test2"
}
println(Await.result(
for {
v1 <- test1
v2 <- test2
} yield { v1 + v2 }, 2 seconds))
}
Theo các bạn thì việc chạy đoạn code trên sẽ diễn ra thế nào, tuần tự hay song song?
Nếu song song thì đoạn code sẽ kết thúc trong 2s, và kết quả chúng ta mong đợi sẽ là "test1test2"
Đây là kết quả khi chạy đoạn code trên:
error java.util.concurrent.TimeoutException: Futures timed out after [2 seconds]
java.util.concurrent.TimeoutException: Futures timed out after [2 seconds]
Như vậy là test1 và test2 không được chạy song song, điều gì đã khiến cho chúng bị diễn ra tuần tự?
Thủ phạm chính là def
. Việc sử dụng def khiến cho việc execute đoạn code bên trong chỉ được diễn ra tại thời điểm gọi, kết hợp với việc for comprehension trong scala lại được compile thành nest flatMap
:
test1 flatMap { v1 => { test2 flatMap { v2 } }
Do đó mà test2 Future được tạo ra sau khi test1 đã được execute xong, khiến cho việc xử lý bị thành tuần tự.
Cách giải quyết khá đơn giản, thay vì dùng def
thì chúng ta dùng val
để tạo Future placeholder trước.
val test1 = Future {
Thread.sleep(2000)
"test1"
}
val test2 =
Future {
Thread.sleep(2000)
"test2"
}
Ngoài ra chúng ta cũng có thê dùng zip
hoặc là Future.sequence
test1 zip test2 onComplete { case Success(v) => v._1 + v._2 }
Future.sequence(List(test1, test2))
Đây là một cái bẫy khá nguy hiểm mà có thể khiến chúng ta có những xử lý tưởng là song song, nhưng thực ra là tuần tự, nên cần chú ý.







