-
Notifications
You must be signed in to change notification settings - Fork 31
Support Scala.js #127
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Support Scala.js #127
Conversation
@@ -30,7 +30,7 @@ import scala.util.boundary | |||
* @see | |||
* [[Async$.group Async.group]] and [[Future$.apply Future.apply]] for [[Async]]-subscoping operations. | |||
*/ | |||
trait Async(using val support: AsyncSupport, val scheduler: support.Scheduler): | |||
trait Async private[async] (using val support: AsyncSupport, val scheduler: support.Scheduler): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should we make it sealed?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Provide an adapter for extension point ?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I am thinking about whether Async
should be extensible or not. By not doing it, we retain more power to reason about it. It will also make it easier to add more to it without breaking changes (for example, context-local data)
Hmm, stress test on CI is much slower on my machine (30s / 80s), should look into it. |
) | ||
) | ||
|
||
def patchJSPIIR(jspiIRFile: File, streams: TaskStreams): Unit = { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Note that this is only to support js.async/await
until the Scala 3 compiler natively supports it
It is not available in the Scala.js context.
... to prevent switching carrier thread on Scala Native.
…tually returns `T` And rename the `blocking` calls we have in the tests accordingly.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Performance is a bit worrying but I think it is ok for merging. I will keep an eye on perf as we iterate.
@@ -41,3 +43,103 @@ lazy val root = | |||
} | |||
) | |||
) | |||
.jsSettings( | |||
Seq( | |||
scalaVersion := "3.7.1-RC1-bin-20250425-fb6cc9b-NIGHTLY", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it should be fine to keep this as-is for now, until 3.7.0 drops.
Resolves infinite initialization loop. We need to check for its nullness when doing a re-link.
Currently using nightly Scala 3 for support for scala.js 1.19.
JSPI async/await is provided with a shim and manual transformation, until they gain compiler support.
Async.blocking
is not really available unless we have top-levelawait
or assume an existingjs.async
scope.To remedy this,
Async.blocking
now requires an instanace of the sealedAsync.FromSync
trait, which can beprovided (and is provided by default) on JVM and Native by initializing
FromSync.BlockingWithLocks
withAsyncSupport
andScheduler
as before.Specifically, the
Async.blocking
function requires an instance of theFromSync.Blocking
trait, which refinesFromSync
with the requirement that the return type when running an asynchronous block isT
.This is not always possible on Scala.js for example, where the default
FromSync
instance given would returnscala.concurrent.Future[T]
instead.There are two ways to get around this and have a top-level program to run on all 3 platforms:
Use
Async.fromSync
, which is the same asAsync.blocking
but without theOutput[T] = T
requirement.One has to be careful that on Scala.js, this would return a
Future[T]
, and the computation might have notcompleted by the time
fromSync
returns.To instead have the blocking behavior, assuming top-level
await
or an existingjs.async
scope, providean instance of
FromSync.Blocking
with thejs.UnsafeJsAsyncFromSync
implementation.Because of the single-threaded nature of JS, listener locks have to be able to suspend an asynchronous computation
instead of blocking the whole thread as possible on JVM/Native. Therefore,
NumberedLock
on scala.js implicitlyassumes top-level
await
or an existingjs.async
scope.This works nicely with
Future
andPromise
s in Gears, as well as interacting directly with JavaScript promises,however manually written
Sources
should be aware of this limitation.Writing spinning-like code under a
Future
may directly block the entire computation. This is not a direct limitationon JS alone (this will also saturate a carrier thread on JVM/Native as well), but has a much more direct effect on
scala.js due to only having a single thread.