diff --git a/js/src/main/scala/cs214/webapp/client/StateMachineClientApp.scala b/js/src/main/scala/cs214/webapp/client/StateMachineClientApp.scala
index 8f70c6b0d257f700164f23430814c1a3d0c840e7..3cab0dbb41e50ac3ed2cce2a6ffe53e461ec1f69 100644
--- a/js/src/main/scala/cs214/webapp/client/StateMachineClientApp.scala
+++ b/js/src/main/scala/cs214/webapp/client/StateMachineClientApp.scala
@@ -7,6 +7,9 @@ import scala.util.{Failure, Success}
 import org.scalajs.dom
 import scalatags.JsDom.all.Frag
 
+import scala.scalajs.concurrent.JSExecutionContext.Implicits.queue
+import scala.concurrent.{Future, Promise}
+
 type Target = dom.Element
 
 abstract class WSClientApp extends ClientApp:
@@ -83,58 +86,41 @@ abstract class StateMachineClientAppInstance[Event, View](
       case Failure(msg)              => WebClient.crash(Exception(msg))
       case Success(Failure(msg))     => renderError(msg.getMessage)
       case Success(Success(actions)) => actionsQueue.enqueueAll(actions)
-    tryProcessNextAction()
+    tryProcessActionsQueue()
 
-  /** The views to render */
+  /** Actions waiting to be processed. */
   private val actionsQueue: mutable.Queue[Action[ujson.Value]] = mutable.Queue()
+  private var processing = false
 
-  /** Minimal cooldown time between the render of two views */
-  private var actionCooldownDelay = 0
-
-  /** The timestamp for the last render of a view */
-  private var lastActionAppliedMs = System.currentTimeMillis()
-
-  /** A call to the [[tryProcessNextAction()]] function scheduled */
-  private var timeout: Option[SetTimeoutHandle] = None
+  private def tryProcessActionsQueue(): Unit =
+    if !processing then processActions()
 
-  /** Sets the [[actionCooldownDelay]] of the client to the specified value */
-  private def setCooldown(ms: Int): Unit = actionCooldownDelay = ms
-
-  /** Apply the next action from [[actionsQueue]] if [[actionCooldownDelay]]
-    * permits it.
-    */
-  private def tryProcessNextAction(): Unit =
-    // If there are still views to render and if no call to this function is scheduled
-    if actionsQueue.nonEmpty && timeout.isEmpty then
-      // Then check if we are out of the cooldown delay
-      if System.currentTimeMillis() - lastActionAppliedMs > actionCooldownDelay then
-        // We can render the view, and dequeue it
-        processAction(actionsQueue.dequeue())
-        // Continue try emptying the queue
-        tryProcessNextAction()
-      else
-        // We still have to wait, put a timeout to call the function later
-        timeout = Some(setTimeout(actionCooldownDelay) {
-          // First remove the timeout so that, if necessary,
-          // the next function call can create a new one
-          timeout = None
-          // Then try to render next views
-          tryProcessNextAction()
-        })
+  private def processActions(): Unit =
+    require(!processing)
+    def loop: Unit =
+      processing = actionsQueue.nonEmpty
+      if processing then
+        processAction(actionsQueue.dequeue()).andThen(_ => loop)
+    loop
 
   /** Execute a single action sent by the server. */
-  private def processAction(jsonAction: Action[ujson.Value]): Unit =
-    lastActionAppliedMs = System.currentTimeMillis()
-    setCooldown(0)
+  private def processAction(jsonAction: Action[ujson.Value]): Future[Unit] =
     jsonAction match
       case Action.Alert(msg) =>
         dom.window.alert(msg)
+        after(0)
       case Action.Pause(durationMs) =>
-        setCooldown(durationMs)
+        after(durationMs)
       case Action.Render(js) =>
-        // Step1: The client receives the view sent by the server here
         wire.viewFormat.decode(js) match
           case Failure(exception) => renderError(exception.getMessage)
           case Success(jsonView) =>
             target.replaceChildren:
               this.render(userId, jsonView).render
+        after(0)
+
+  /* Like `setTimeout(interval)`, but return a `Future`. */
+  private def after(interval: Double): Future[Unit] =
+    val pr = Promise[Unit]()
+    setTimeout(interval)(pr.success(()))
+    pr.future