diff --git a/packages/compiler/test/compiler.test.ts b/packages/compiler/test/compiler.test.ts
index 4386672e..41b68de6 100644
--- a/packages/compiler/test/compiler.test.ts
+++ b/packages/compiler/test/compiler.test.ts
@@ -881,7 +881,7 @@ component Accordion {
expect(output).not.toContain('${(isOpen(index)) ? " open" : ""}');
});
-test("server-rendered each locals work in reactive handlers", () => {
+test("server-rendered each locals work in reactive handlers", async () => {
const firstLocals = Buffer.from(
JSON.stringify({
item: {
@@ -903,13 +903,12 @@ test("server-rendered each locals work in reactive handlers", () => {
).toString("base64");
const dom = mountHtml(`
-
+
`);
- const buttons: any = dom.querySelectorAll("button");
+ const buttons = dom.querySelectorAll("button") as HTMLButtonElement[];
- buttons[1].click();
+ buttons[1]!.click();
+
+ await Promise.resolve();
const articles = dom.querySelectorAll("article");
- expect(articles[0].querySelector("span")?.classList.contains("open")).toBe(false);
+ expect(articles[0]!.querySelector("span")?.classList.contains("open")).toBe(false);
- expect(articles[1].querySelector("span")?.classList.contains("open")).toBe(true);
+ expect(articles[1]!.querySelector("span")?.classList.contains("open")).toBe(true);
});
diff --git a/packages/csr/src/reactive-runtime.ts b/packages/csr/src/reactive-runtime.ts
index 7a62c819..7079b5b7 100644
--- a/packages/csr/src/reactive-runtime.ts
+++ b/packages/csr/src/reactive-runtime.ts
@@ -214,59 +214,6 @@ export const REACTIVE_RUNTIME = String.raw`
var value = initial;
var subscribers = new Set();
var notifying = false;
- var pendingNotification = false;
- var pendingPrevious;
-
- function notify(previous) {
- if (notifying) {
- pendingNotification = true;
-
- if (pendingPrevious === undefined) {
- pendingPrevious = previous;
- }
-
- return;
- }
-
- notifying = true;
-
- try {
- /*
- * Iterate a snapshot. Reactive renderers may subscribe again while
- * running, but that must not mutate the collection currently being
- * iterated.
- */
- var snapshot =
- Array.from(subscribers);
-
- snapshot.forEach(function (
- subscriber,
- ) {
- if (
- subscribers.has(
- subscriber,
- )
- ) {
- subscriber(
- value,
- previous,
- );
- }
- });
- } finally {
- notifying = false;
- }
-
- if (pendingNotification) {
- var nextPrevious =
- pendingPrevious;
-
- pendingNotification = false;
- pendingPrevious = undefined;
-
- notify(nextPrevious);
- }
- }
return {
get: function () {
@@ -286,7 +233,46 @@ export const REACTIVE_RUNTIME = String.raw`
var previous = value;
value = nextValue;
- notify(previous);
+ /*
+ * Prevent nested synchronous notification of the
+ * same signal.
+ */
+ if (notifying) {
+ return;
+ }
+
+ notifying = true;
+
+ try {
+ /*
+ * Always iterate a snapshot. A renderer can read
+ * this signal and subscribe again while it runs.
+ */
+ var snapshot =
+ Array.from(subscribers);
+
+ for (
+ var index = 0;
+ index < snapshot.length;
+ index++
+ ) {
+ var subscriber =
+ snapshot[index];
+
+ if (
+ subscribers.has(
+ subscriber,
+ )
+ ) {
+ subscriber(
+ value,
+ previous,
+ );
+ }
+ }
+ } finally {
+ notifying = false;
+ }
},
subscribe: function (