Announcing Scala.js 1.19.0
Apr 21, 2025.
We are pleased to announce the release of Scala.js 1.19.0!
This release comes with significant performance improvements to the WebAssembly backend. For codebase where performance is dominated by computations (rather than JS interop), you can now expect the Wasm output to be faster than the JS output.
Moreover, as of this writing, the latest versions of Firefox (since v131) and Safari (since v18.4) support all the WebAssembly features required to run Scala.js-on-Wasm. Chrome still requires a flag to enable exception handling.
If you haven’t tried the WebAssembly target yet, now is a good time to do so!
Other highlights:
- Native support for JavaScript
async/await
, throughjs.async { ... }
andjs.await(...)
. - Small code size improvements for the JavaScript target when using SAM lambdas.
- For Wasm only: support for the JavaScript Promise Integration feature (JSPI).
Read on for more details.
Getting started
If you are new to Scala.js, head over to the tutorial.
If you need help with anything related to Scala.js, you may find our community in #scala-js
on Discord and on Stack Overflow.
Bug reports can be filed on GitHub.
Release notes
If upgrading from Scala.js 0.6.x, make sure to read the release notes of Scala.js 1.0.0 first, as they contain a host of important information, including breaking changes.
This is a minor release:
- It is backward binary compatible with all earlier versions in the 1.x series: libraries compiled with 1.0.x through 1.18.x can be used with 1.19.0 without change.
- It is not forward binary compatible with 1.18.x: libraries compiled with 1.19.0 cannot be used with 1.18.x or earlier.
- It is not entirely backward source compatible: it is not guaranteed that a codebase will compile as is when upgrading from 1.18.x (in particular in the presence of
-Xfatal-warnings
).
As a reminder, libraries compiled with 0.6.x cannot be used with Scala.js 1.x; they must be republished with 1.x first.
Enhancements with compatibility concerns
Drop support for non-strict floats
Support for non-strict Float
s was deprecated 3 years ago in Scala.js 1.9.0.
We now removed that support.
Builds configured to use non-strict floats (with withStrictFloats(false)
) will refuse to compile.
If that setting is used through an indirect dependency, it will be silently ignored.
Strict floats are almost entirely backward compatible with non-strict floats.
In general, strict floats mandate behavior which non-strict floats leave unspecified (so non-strict floats were always permitted to behave like strict floats).
The only exception is that tests of the form x.isInstanceOf[Float]
(or case x: Float =>
) will answer false
for number values that cannot exactly be represented in a 32-bit Float
.
We are not aware of any codebase that was ever adversely affected by strict float semantics.
Deprecate support for targeting ECMAScript 5.1
The support for targeting ECMAScript 5.1 is currently the biggest source of alternative code paths and polyfills in our codebase. Moreover, the ES 5.1 output does not even have exactly the same semantics as later versions:
- JS classes are not true classes. Notably, that means they cannot extend native ES classes, and they do not inherit static members.
- Top-level exports are declared as vars instead of lets.
10 years after the introduction of ECMAScript 2015, we believe it is time to deprecate the support for targeting ES 5.1, so that we can eventually remove it. The removal will happen in a future major or minor release of Scala.js.
Changes to the IR and linker APIs
For tooling authors who directly manipulate the IR and linker APIs, there have been some breaking changes in that area. This is in line with our version policy for the linker APIs.
The most likely changes you may hit are:
- The
ir.Trees.Closure
node has two additional fields:flags
andresultType
. - All the “well-known” names that were previously in
ir.Names
have been moved to a new objectir.WellKnownNames
. ir.Types.PrimRef
is not a case class anymore.
Enhancements
Native support for JS async/await
Note 1: Scala 3 users will have to wait for a future release of Scala 3—likely 3.8.0—to be able to use this new feature.
Note 2: when targeting WebAssembly, this feature requires support for JSPI (JavaScript Promise Integration).
You can check support for JSPI in various browsers here.
On Node.js v23+, this requires the flag --experimental-wasm-jspi
.
This release adds a pair of primitives, js.async
and js.await
, which correspond to the async/await
feature of JavaScript.
In order to use these functions, you must configure Scala.js to target ECMAScript 2017 or later.
In an sbt build, use the following setting:
// Target ES 2017 to enable async/await
scalaJSLinkerConfig := {
scalaJSLinkerConfig.value
.withESFeatures(_.withESVersion(ESVersion.ES2017)) // enable async/await
},
You can then use js.async { ... }
blocks containing js.await
calls.
For example:
val p: js.Promise[String] = downloadSomething()
val result: js.Promise[Int] = js.async {
val text: String = js.await(p)
text.toInt
}
js.async { ... }
executes a block of code under a JavaScript async
context.
The block of code can await js.Promise
s using js.await
.
Doing so will continue the execution after the call to js.await
when the given Promise
is resolved.
If the Promise
is rejected, the exception gets rethrown at the call site.
A block such as js.async { body }
is equivalent to an immediately-applied JavaScript async
function:
(async () => body)()
js.async
returns a js.Promise
that will be resolved with the result of the code block.
If the block throws an exception, the Promise
will be rejected.
Calls to js.await
can only appear within a js.async
block.
They must not be nested in any local method, class, by-name argument or closure.
The latter includes for
comprehensions.
They may appear within conditional branches, while
loops and try/catch/finally
blocks.
Orphan await
s in WebAssembly
When compiling for Scala.js-on-Wasm only, you can allow calls to js.await
anywhere, by adding the following import:
import scala.scalajs.js.wasm.JSPI.allowOrphanJSAwait
Calls to orphan js.await
s are validated at run-time.
There must exist a dynamically enclosing js.async { ... }
block on the call stack.
Moreover, there cannot be any JavaScript frame (JavaScript function invocation) in the call stack between the js.async { ... }
block and the call to js.await
.
If those conditions are not met, a JavaScript exception of type WebAssembly.SuspendError
gets thrown.
The ability to detach js.await
calls from their corresponding js.async
block is a new superpower offered by JSPI.
It means that, as long as you enter into a js.async
block somewhere, you can synchronously await Promise
s in any arbitrary function!
This is a power of the same sort as Loom on the JVM.
We are looking forward to seeing new libraries built on these primitives to offer efficient and straightforward APIs.
Performance improvements for WebAssembly
Scala.js 1.19.0 brings significant performance improvements to the WebAssembly output. Starting with this release, the WebAssembly output is faster than the JavaScript output on our benchmarks (geomean of the running time is 15% lower).
Note that our benchmarks do not measure JavaScript interop in any significant way. If your application’s performance depends mostly on JS interop, the JavaScript output is probably still faster (and will remain so for the foreseeable future). However, if your bottleneck is in computations inside Scala code, compiling to WebAssembly should now give you a free performance boost.
The WebAssembly target also received additional improvements in terms of code size and run-time memory consumption.
As a reminder, you may read detailed information about the WebAssembly backend in the docs.
Miscellaneous
New JDK APIs
This release adds support for the following JDK methods:
- Most of
java.util.random.RandomGenerator
Improvements to the JUnit interface
Thanks to @dubinsky
for contributing two improvements to the integration of our JUnit implementation with external tooling (in particular Gradle):
- populate
sbt.testing.Event.throwable
on test failure, and - populate
sbt.testing.Event.duration
.
Bug fixes
The following bugs have been fixed in 1.19.0:
- #5131 Linker not noticing instance test changes
- #5135 Deadlock in concurrent initialization of
ir.Names
andir.Types
.
You can find the full list on GitHub.