Skip to content

Commit c2d1c4a

Browse files
committed
js: simplify onMessage
We now don't care about events sent during startup. We also don't treat name registration specially. This removes a lot of state.
1 parent 1d2dd36 commit c2d1c4a

File tree

4 files changed

+12
-63
lines changed

4 files changed

+12
-63
lines changed

popcorn/elixir/pages/elixir-api.md

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,11 +15,13 @@ Represents opaque reference to JS value. Automatically cleaned up when garbage c
1515

1616
### `send_event(event_name, payload \\ nil)`
1717

18-
Sends an event to the JS side. Fire-and-forget.
18+
Sends an event to the JS side. Fire-and-forget. Events with names starting with `popcorn` are reserved for internal use.
19+
20+
Events sent during startup (before `Popcorn.init()` resolves on the JS side) are not delivered to `onMessage` handlers.
1921

2022
**Parameters:**
2123

22-
- `event_name` (string) - Name of the event.
24+
- `event_name` (string) - Name of the event. Must not start with `popcorn`.
2325
- `payload` (any, optional) - Event payload. Default: `nil`.
2426

2527
**Returns:** `:ok`.

popcorn/elixir/test/support/atomvm.ex

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -164,7 +164,7 @@ defmodule Popcorn.Support.AtomVM do
164164

165165
snippet = """
166166
try {
167-
const result = await popcorn.call("#{args}", {});
167+
const result = await popcorn.call("#{args}", {process: "main"});
168168
return result;
169169
} catch (e) {
170170
return {error: true}

popcorn/js/src/popcorn.ts

Lines changed: 7 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@ import {
66
MAX_RELOAD_N,
77
MESSAGES,
88
EVENT_NAMES,
9-
DEFAULT_RECEIVER_TIMEOUT_MS,
109
} from "./types";
1110
import { PopcornError, PopcornInternalError, throwError } from "./errors";
1211

@@ -35,8 +34,6 @@ export type PopcornInitOptions = {
3534
wasmDir?: string;
3635
/** Enable debug logging. */
3736
debug?: boolean;
38-
/** Catch-all event handler, active from iframe start. Only receives user events (not popcorn_ prefixed). */
39-
onMessage?: (eventName: string, payload: AnySerializable) => void;
4037
};
4138

4239
/** Options for cast method */
@@ -114,11 +111,7 @@ export class Popcorn {
114111
};
115112

116113
private messageHandlers = new Set<MessageHandler>();
117-
private initOnMessage: MessageHandler | null = null;
118114
private mountResolve: (() => void) | null = null;
119-
private mounted = false;
120-
private defaultReceiverWaiter: Promise<string | null> | null = null;
121-
private resolveDefaultReceiver: ((name: string) => void) | null = null;
122115
private heartbeatTimeout: ReturnType<typeof setTimeout> | null = null;
123116
private reloadN = 0;
124117

@@ -134,7 +127,6 @@ export class Popcorn {
134127
this.onReloadCallback = params.onReload ?? noop;
135128
this.debug = params.debug ?? false;
136129
this.bundleURL = bundleURL.href;
137-
this.initOnMessage = params.onMessage ?? null;
138130

139131
this.bridgeConfig = {
140132
container: params.container,
@@ -203,7 +195,6 @@ export class Popcorn {
203195
]);
204196
clearTimeout(initTimeout);
205197

206-
this.mounted = true;
207198
this.transition({ status: "ready" });
208199
this.trace("Main: mounted");
209200
this.onHeartbeat();
@@ -234,6 +225,8 @@ export class Popcorn {
234225
{ process, timeoutMs }: CallOptions = {},
235226
): Promise<CallResult> {
236227
this.assertStatus(["ready"]);
228+
const targetProcess = process ?? this.defaultReceiver;
229+
if (targetProcess === null) throwError({ t: "bad_target" });
237230
if (this.bridge === null) throwError({ t: "unmounted" });
238231

239232
const requestId = this.requestId++;
@@ -242,20 +235,11 @@ export class Popcorn {
242235
this.calls.set(requestId, { acknowledged: false, startTimeMs, resolve });
243236
});
244237

245-
const targetProcess = await this.resolveTargetProcess(process);
246-
247-
if (targetProcess === null) {
248-
this.calls.delete(requestId);
249-
throwError({ t: "bad_target" });
250-
}
251-
252-
if (this.bridge !== null) {
253-
this.trace("Main: call: ", { requestId, process, args });
254-
this.bridge.sendIframeRequest({
255-
type: MESSAGES.CALL,
256-
value: { requestId, process: targetProcess, args },
257-
});
258-
}
238+
this.trace("Main: call: ", { requestId, process, args });
239+
this.bridge.sendIframeRequest({
240+
type: MESSAGES.CALL,
241+
value: { requestId, process: targetProcess, args },
242+
});
259243

260244
const result = await withTimeout(callPromise, timeoutMs ?? CALL_TIMEOUT_MS);
261245
this.calls.delete(requestId);
@@ -290,41 +274,18 @@ export class Popcorn {
290274
this.trace("Main: deinit");
291275
this.transition({ status: "deinit" });
292276
this.teardownBridge("deinitialized");
293-
this.initOnMessage = null;
294277
this.logListeners.stdout.clear();
295278
this.logListeners.stderr.clear();
296279
this.messageHandlers.clear();
297280
}
298281

299-
private async resolveTargetProcess(
300-
process: string | undefined,
301-
): Promise<string | null> {
302-
const target = process ?? this.defaultReceiver;
303-
if (target !== null) return target;
304-
305-
if (this.defaultReceiverWaiter === null) {
306-
this.defaultReceiverWaiter = new Promise<string | null>((resolve) => {
307-
this.resolveDefaultReceiver = resolve;
308-
setTimeout(() => {
309-
this.resolveDefaultReceiver = null;
310-
resolve(null);
311-
}, DEFAULT_RECEIVER_TIMEOUT_MS);
312-
});
313-
}
314-
315-
return this.defaultReceiverWaiter;
316-
}
317-
318282
private teardownBridge(errorCode: "deinitialized" | "reload") {
319283
if (this.bridge) {
320284
this.bridge.deinit();
321285
this.bridge = null;
322286
}
323287
this.mountResolve = null;
324-
this.mounted = false;
325288
this.defaultReceiver = null;
326-
this.defaultReceiverWaiter = null;
327-
this.resolveDefaultReceiver = null;
328289
if (this.heartbeatTimeout) {
329290
clearTimeout(this.heartbeatTimeout);
330291
this.heartbeatTimeout = null;
@@ -377,25 +338,12 @@ export class Popcorn {
377338
this.mountResolve = null;
378339
} else if (eventName === EVENT_NAMES.SET_DEFAULT_RECEIVER) {
379340
this.defaultReceiver = payload.name;
380-
this.resolveDefaultReceiver?.(payload.name);
381-
this.resolveDefaultReceiver = null;
382341
} else {
383342
this.trace("Unknown internal event:", eventName);
384343
}
385344
return;
386345
}
387346

388-
if (!this.mounted) {
389-
this.initOnMessage?.(eventName, payload);
390-
}
391-
392-
this.dispatchToHandlers(eventName, payload);
393-
}
394-
395-
private dispatchToHandlers(
396-
eventName: string,
397-
payload: AnySerializable,
398-
): void {
399347
this.messageHandlers.forEach((handler) => {
400348
try {
401349
handler(eventName, payload);

popcorn/js/src/types.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@ export const CALL_TIMEOUT_MS = 60_000;
33
export const HEARTBEAT_TIMEOUT_MS = 60_000;
44
export const HEARTBEAT_INTERVAL_MS = 500;
55
export const MAX_RELOAD_N = 3;
6-
export const DEFAULT_RECEIVER_TIMEOUT_MS = 5_000;
76

87
// eslint-disable-next-line @typescript-eslint/no-explicit-any
98
export type AnySerializable = any;

0 commit comments

Comments
 (0)