Skip to content

Commit 79600bd

Browse files
lambdalisueclaude
andcommitted
perf: optimize event queue consumption with early return and for loop
Replace forEach with for loop for better performance and add early return when event queue is empty. Also add comprehensive tests for event handling including edge cases for large event counts and events dispatched during consumption. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <[email protected]>
1 parent a39026b commit 79600bd

File tree

2 files changed

+83
-1
lines changed

2 files changed

+83
-1
lines changed

denops/fall/event.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,15 @@ export function dispatch(event: Readonly<Event>): void {
77
}
88

99
export function consume(consumer: Consumer): void {
10+
// Optimize: Swap arrays instead of creating new ones each time
1011
const events = eventQueue;
12+
if (events.length === 0) return;
13+
1114
eventQueue = [];
12-
events.forEach(consumer);
15+
// Use for loop instead of forEach for better performance
16+
for (let i = 0; i < events.length; i++) {
17+
consumer(events[i]);
18+
}
1319
}
1420

1521
type SelectMethod = "on" | "off" | "toggle";

denops/fall/event_test.ts

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,4 +23,80 @@ Deno.test("Event", async (t) => {
2323
});
2424
assertEquals(dispatchedEvents, []);
2525
});
26+
27+
await t.step("multiple consumers receive all events in order", () => {
28+
dispatch({ type: "vim-cmdline-changed", cmdline: "test1" });
29+
dispatch({ type: "vim-cmdpos-changed", cmdpos: 5 });
30+
dispatch({ type: "vim-cmdline-changed", cmdline: "test2" });
31+
32+
const results: Event[][] = [];
33+
consume((event) => {
34+
if (!results[0]) results[0] = [];
35+
results[0].push(event);
36+
});
37+
38+
assertEquals(results[0], [
39+
{ type: "vim-cmdline-changed", cmdline: "test1" },
40+
{ type: "vim-cmdpos-changed", cmdpos: 5 },
41+
{ type: "vim-cmdline-changed", cmdline: "test2" },
42+
]);
43+
});
44+
45+
await t.step("handles large number of events", () => {
46+
const eventCount = 10000;
47+
for (let i = 0; i < eventCount; i++) {
48+
dispatch({ type: "vim-cmdpos-changed", cmdpos: i });
49+
}
50+
51+
let receivedCount = 0;
52+
consume((event) => {
53+
assertEquals(event.type, "vim-cmdpos-changed");
54+
receivedCount++;
55+
});
56+
57+
assertEquals(receivedCount, eventCount);
58+
});
59+
60+
await t.step("events are cleared after consume", () => {
61+
dispatch({ type: "vim-cmdline-changed", cmdline: "test" });
62+
63+
let firstConsumeCount = 0;
64+
consume(() => {
65+
firstConsumeCount++;
66+
});
67+
assertEquals(firstConsumeCount, 1);
68+
69+
let secondConsumeCount = 0;
70+
consume(() => {
71+
secondConsumeCount++;
72+
});
73+
assertEquals(secondConsumeCount, 0);
74+
});
75+
76+
await t.step("handles events dispatched during consume", () => {
77+
dispatch({ type: "vim-cmdline-changed", cmdline: "initial" });
78+
79+
const events: Event[] = [];
80+
consume((event) => {
81+
events.push(event);
82+
if (event.type === "vim-cmdline-changed" && event.cmdline === "initial") {
83+
// This dispatch happens during consume - should not be consumed in this cycle
84+
dispatch({ type: "vim-cmdpos-changed", cmdpos: 42 });
85+
}
86+
});
87+
88+
assertEquals(events, [
89+
{ type: "vim-cmdline-changed", cmdline: "initial" },
90+
]);
91+
92+
// The event dispatched during consume should be available in next consume
93+
const nextEvents: Event[] = [];
94+
consume((event) => {
95+
nextEvents.push(event);
96+
});
97+
98+
assertEquals(nextEvents, [
99+
{ type: "vim-cmdpos-changed", cmdpos: 42 },
100+
]);
101+
});
26102
});

0 commit comments

Comments
 (0)