From 632478a37fa2b8b2a5b29d7a5bed728ce541450a Mon Sep 17 00:00:00 2001 From: Arman Bilge Date: Wed, 26 Feb 2025 15:38:17 +0000 Subject: [PATCH 1/2] Avoid allocating `Runnable` wrapper --- .../macrotaskexecutor/MacrotaskExecutor.scala | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/core/src/main/scala/org/scalajs/macrotaskexecutor/MacrotaskExecutor.scala b/core/src/main/scala/org/scalajs/macrotaskexecutor/MacrotaskExecutor.scala index db40e23..3d42cf9 100644 --- a/core/src/main/scala/org/scalajs/macrotaskexecutor/MacrotaskExecutor.scala +++ b/core/src/main/scala/org/scalajs/macrotaskexecutor/MacrotaskExecutor.scala @@ -29,15 +29,15 @@ object MacrotaskExecutor extends ExecutionContextExecutor { private[this] final val Undefined = "undefined" def execute(runnable: Runnable): Unit = - setImmediate(() => runnable.run()) + setImmediate(runnable) def reportFailure(cause: Throwable): Unit = cause.printStackTrace() - private[this] val setImmediate: (() => Unit) => Unit = { + private[this] val setImmediate: Runnable => Unit = { if (js.typeOf(js.Dynamic.global.setImmediate) == Undefined) { var nextHandle = 1 - val tasksByHandle = mutable.Map[Int, () => Unit]() + val tasksByHandle = mutable.Map[Int, Runnable]() var currentlyRunningATask = false def canUsePostMessage(): Boolean = { @@ -71,7 +71,7 @@ object MacrotaskExecutor extends ExecutionContextExecutor { case Some(task) => currentlyRunningATask = true try { - task() + task.run() } finally { tasksByHandle -= handle currentlyRunningATask = false @@ -95,7 +95,7 @@ object MacrotaskExecutor extends ExecutionContextExecutor { js.Dynamic.global.Node.constructor("return setImmediate")() { k => - setImmediate(k) + setImmediate(() => k.run()) () } } else if (canUsePostMessage()) { @@ -153,13 +153,13 @@ object MacrotaskExecutor extends ExecutionContextExecutor { // we're also not going to bother fast-pathing for IE6; just fall through { k => - js.Dynamic.global.setTimeout(k, 0) + js.Dynamic.global.setTimeout(() => k.run(), 0) () } } } else { { k => - js.Dynamic.global.setImmediate(k) + js.Dynamic.global.setImmediate(() => k.run()) () } } From af120b0cd1f3609e73288783603e2d11ce4defe5 Mon Sep 17 00:00:00 2001 From: Arman Bilge Date: Wed, 26 Feb 2025 20:06:04 +0000 Subject: [PATCH 2/2] Use ordinary JS object instead of Scala `Map` --- .../macrotaskexecutor/MacrotaskExecutor.scala | 42 ++++++++++--------- 1 file changed, 23 insertions(+), 19 deletions(-) diff --git a/core/src/main/scala/org/scalajs/macrotaskexecutor/MacrotaskExecutor.scala b/core/src/main/scala/org/scalajs/macrotaskexecutor/MacrotaskExecutor.scala index 3d42cf9..f1916bf 100644 --- a/core/src/main/scala/org/scalajs/macrotaskexecutor/MacrotaskExecutor.scala +++ b/core/src/main/scala/org/scalajs/macrotaskexecutor/MacrotaskExecutor.scala @@ -16,9 +16,9 @@ package org.scalajs.macrotaskexecutor -import scala.collection.mutable import scala.concurrent.{ExecutionContext, ExecutionContextExecutor} import scala.scalajs.js +import scala.scalajs.js.annotation._ import scala.util.Random import scala.util.control.NonFatal @@ -34,10 +34,18 @@ object MacrotaskExecutor extends ExecutionContextExecutor { def reportFailure(cause: Throwable): Unit = cause.printStackTrace() + @js.native + private[this] trait TaskMap extends js.Object { + @JSBracketAccess + def apply(handle: Int): Runnable + @JSBracketAccess + def update(handle: Int, task: Runnable): Unit + } + private[this] val setImmediate: Runnable => Unit = { if (js.typeOf(js.Dynamic.global.setImmediate) == Undefined) { var nextHandle = 1 - val tasksByHandle = mutable.Map[Int, Runnable]() + val tasksByHandle = (new js.Object).asInstanceOf[TaskMap] var currentlyRunningATask = false def canUsePostMessage(): Boolean = { @@ -63,21 +71,17 @@ object MacrotaskExecutor extends ExecutionContextExecutor { } } - def runIfPresent(handle: Int): Unit = { + def runTaskForHandle(handle: Int): Unit = { if (currentlyRunningATask) { - js.Dynamic.global.setTimeout(() => runIfPresent(handle), 0) + js.Dynamic.global.setTimeout(() => runTaskForHandle(handle), 0) } else { - tasksByHandle.get(handle) match { - case Some(task) => - currentlyRunningATask = true - try { - task.run() - } finally { - tasksByHandle -= handle - currentlyRunningATask = false - } - - case None => + val task = tasksByHandle(handle) + currentlyRunningATask = true + try { + task.run() + } finally { + js.special.delete(tasksByHandle, handle) + currentlyRunningATask = false } } @@ -115,7 +119,7 @@ object MacrotaskExecutor extends ExecutionContextExecutor { .data .indexOf(messagePrefix) .asInstanceOf[Int] == 0) { - runIfPresent(event.data.toString.substring(messagePrefix.length).toInt) + runTaskForHandle(event.data.toString.substring(messagePrefix.length).toInt) } } @@ -129,7 +133,7 @@ object MacrotaskExecutor extends ExecutionContextExecutor { val handle = nextHandle nextHandle += 1 - tasksByHandle += (handle -> k) + tasksByHandle(handle) = k js.Dynamic.global.postMessage(messagePrefix + handle, "*") () } @@ -137,14 +141,14 @@ object MacrotaskExecutor extends ExecutionContextExecutor { val channel = js.Dynamic.newInstance(js.Dynamic.global.MessageChannel)() channel.port1.onmessage = { (event: js.Dynamic) => - runIfPresent(event.data.asInstanceOf[Int]) + runTaskForHandle(event.data.asInstanceOf[Int]) } { k => val handle = nextHandle nextHandle += 1 - tasksByHandle += (handle -> k) + tasksByHandle(handle) = k channel.port2.postMessage(handle) () }