Scala及JS中Future/Await与Async/Await的实现差异是什么?
Await.result and Async/Await (Scala/JavaScript) Great question! Let’s break down the core distinctions between the Await.result approach you’ve shared and the standard Async/Await pattern in both Scala and JavaScript, using your code examples as a starting point.
1. Parallel Execution Behavior
First, let’s look at how asynchronous tasks run in each approach:
Your Scala Await.result Implementation
def b1() = Future { 1 } def b2() = Future { 2 } def a() = Future { Await.result(b1(), Duration.Inf) + Await.result(b2(), Duration.Inf) }
Here’s what happens:
- When
Await.result(b1(), ...)runs, it first starts theb1()Future, then blocks the current thread untilb1()completes. - Only after
b1()finishes does the code callb2()to start the second Future, then blocks again untilb2()completes. - Result:
b1()andb2()run serially, not in parallel.
JavaScript Async/Await Example
async function b1() { return 1 } async function b2() { return 3 } async function a() { return await b1() + await b2() }
Wait, this also runs serially? Yes—because await b1() pauses execution until b1() resolves, then b2() is called afterward. But the Async/Await pattern makes it trivial to switch to parallel execution by starting both tasks first:
async function a() { const p1 = b1(); // Start b1 immediately const p2 = b2(); // Start b2 immediately return await p1 + await p2; // Wait for both to finish (runs in parallel) }
In Scala, the equivalent Async/Await (using the scala.concurrent.async library) works the same way:
import scala.async.Async.{async, await} def a() = async { val f1 = b1() val f2 = b2() await(f1) + await(f2) // Parallel execution }
This is way cleaner than using zip+map for complex logic, though zip+map is a common non-blocking alternative for simple cases:
def a() = b1().zip(b2()).map { case (res1, res2) => res1 + res2 }
2. Blocking vs. Non-Blocking Execution
Await.result: This is a blocking operation. It halts the current thread until the Future completes. In async-heavy applications (like Play or Akka HTTP), blocking threads wastes resources and kills throughput—you’re tying up a thread that could be handling other tasks.- Async/Await: Both JavaScript and Scala’s Async/Await are non-blocking. They’re syntactic sugar over callback chains (Promises in JS,
map/flatMapin Scala’s Futures). Instead of blocking a thread, the runtime schedules the rest of the code to run once the async task finishes, freeing up the thread for other work.
3. Code Readability & Maintainability
As your async logic grows more complex (e.g., dependent tasks, multiple steps), the differences become starker:
Await.result: Nested blocking calls get messy quickly, forcing you to wrap everything intry/catchblocks for error handling and making the flow hard to follow.- Async/Await: You write code that looks almost identical to synchronous logic. Dependent tasks read like a linear sequence, and error handling uses familiar
try/catchpatterns.
For example, here’s a multi-step workflow:
Await.result (Ugly, Blocking)
def a() = Future { try { val res1 = Await.result(b1(), Duration.Inf) val res2 = Await.result(b2(res1), Duration.Inf) val res3 = Await.result(b3(res2), Duration.Inf) res3 } catch { case e: Exception => // Handle error } }
Async/Await (Clean, Non-Blocking)
def a() = async { try { val res1 = await(b1()) val res2 = await(b2(res1)) val res3 = await(b3(res2)) res3 } catch { case e: Exception => // Handle error naturally } }
4. Error Handling Integration
Await.result: If a Future fails,Await.resultthrows an exception immediately. You have to manually wrap every call intry/catchto handle failures, which breaks the Future’s native error propagation model.- Async/Await: Errors are seamlessly integrated with the underlying async mechanism. In JS, a failed Promise will trigger a
catchblock in the async function. In Scala, exceptions thrown inside anasyncblock are automatically converted into a failed Future, aligning with how Futures handle errors natively.
内容的提问来源于stack exchange,提问作者KingOfCoders

