mirror of
https://github.com/immich-app/immich.git
synced 2026-01-15 22:42:31 -08:00
perf - replace broadcast channel with direct postMessage
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Low-level protocol for communicating with the service worker via BroadcastChannel.
|
||||
* Low-level protocol for communicating with the service worker via postMessage.
|
||||
*
|
||||
* Protocol:
|
||||
* 1. Main thread sends request: { type: string, requestId: string, ...data }
|
||||
@@ -16,19 +16,20 @@ interface PendingRequest {
|
||||
}
|
||||
|
||||
export class ServiceWorkerMessenger {
|
||||
readonly #broadcast: BroadcastChannel;
|
||||
readonly #pendingRequests = new Map<string, PendingRequest>();
|
||||
readonly #ackTimeoutMs: number;
|
||||
#requestCounter = 0;
|
||||
#onTimeout?: (type: string, data: Record<string, unknown>) => void;
|
||||
|
||||
constructor(channelName: string, ackTimeoutMs = 5000) {
|
||||
this.#broadcast = new BroadcastChannel(channelName);
|
||||
constructor(_channelName: string, ackTimeoutMs = 5000) {
|
||||
this.#ackTimeoutMs = ackTimeoutMs;
|
||||
|
||||
this.#broadcast.addEventListener('message', (event) => {
|
||||
this.#handleMessage(event.data);
|
||||
});
|
||||
// Listen for messages from the service worker
|
||||
if ('serviceWorker' in navigator) {
|
||||
navigator.serviceWorker.addEventListener('message', (event) => {
|
||||
this.#handleMessage(event.data);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
#handleMessage(data: unknown) {
|
||||
@@ -107,7 +108,10 @@ export class ServiceWorkerMessenger {
|
||||
ackReceived: false,
|
||||
});
|
||||
|
||||
this.#broadcast.postMessage({
|
||||
// Send message to the active service worker
|
||||
// Feature detection is done in constructor and at call sites (sw-messaging.ts:isValidSwContext)
|
||||
// eslint-disable-next-line compat/compat
|
||||
navigator.serviceWorker.controller?.postMessage({
|
||||
type,
|
||||
requestId,
|
||||
...data,
|
||||
@@ -135,9 +139,12 @@ export class ServiceWorkerMessenger {
|
||||
}
|
||||
|
||||
/**
|
||||
* Close the broadcast channel
|
||||
* Clean up pending requests
|
||||
*/
|
||||
close(): void {
|
||||
this.#broadcast.close();
|
||||
for (const pending of this.#pendingRequests.values()) {
|
||||
clearTimeout(pending.ackTimeout);
|
||||
}
|
||||
this.#pendingRequests.clear();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
/// <reference no-default-lib="true"/>
|
||||
/// <reference lib="esnext" />
|
||||
/// <reference lib="webworker" />
|
||||
import { installBroadcastChannelListener } from './broadcast-channel';
|
||||
import { installMessageListener } from './messaging';
|
||||
import { handleFetch as handleAssetFetch } from './request';
|
||||
|
||||
const ASSET_REQUEST_REGEX = /^\/api\/assets\/[a-f0-9-]+\/(original|thumbnail)/;
|
||||
@@ -34,4 +34,4 @@ const handleFetch = (event: FetchEvent): void => {
|
||||
sw.addEventListener('install', handleInstall, { passive: true });
|
||||
sw.addEventListener('activate', handleActivate, { passive: true });
|
||||
sw.addEventListener('fetch', handleFetch, { passive: true });
|
||||
installBroadcastChannelListener();
|
||||
installMessageListener();
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
import { handleCancel, handlePrepare } from './request';
|
||||
|
||||
const sw = globalThis as unknown as ServiceWorkerGlobalScope;
|
||||
|
||||
/**
|
||||
* Send acknowledgment for a request
|
||||
*/
|
||||
function sendAck(broadcast: BroadcastChannel, requestId: string) {
|
||||
broadcast.postMessage({
|
||||
function sendAck(client: Client, requestId: string) {
|
||||
client.postMessage({
|
||||
type: 'ack',
|
||||
requestId,
|
||||
});
|
||||
@@ -13,23 +15,21 @@ function sendAck(broadcast: BroadcastChannel, requestId: string) {
|
||||
/**
|
||||
* Handle 'prepare' request: prepare SW to track this request for cancelation
|
||||
*/
|
||||
const handlePrepareRequest = (broadcast: BroadcastChannel, url: URL, requestId: string) => {
|
||||
sendAck(broadcast, requestId);
|
||||
const handlePrepareRequest = (client: Client, url: URL, requestId: string) => {
|
||||
sendAck(client, requestId);
|
||||
handlePrepare(url);
|
||||
};
|
||||
|
||||
/**
|
||||
* Handle 'cancel' request: cancel a pending request
|
||||
*/
|
||||
const handleCancelRequest = (broadcast: BroadcastChannel, url: URL, requestId: string) => {
|
||||
sendAck(broadcast, requestId);
|
||||
const handleCancelRequest = (client: Client, url: URL, requestId: string) => {
|
||||
sendAck(client, requestId);
|
||||
handleCancel(url);
|
||||
};
|
||||
|
||||
export const installBroadcastChannelListener = () => {
|
||||
const broadcast = new BroadcastChannel('immich');
|
||||
// eslint-disable-next-line unicorn/prefer-add-event-listener
|
||||
broadcast.onmessage = (event) => {
|
||||
export const installMessageListener = () => {
|
||||
sw.addEventListener('message', (event) => {
|
||||
if (!event.data?.requestId) {
|
||||
return;
|
||||
}
|
||||
@@ -40,16 +40,21 @@ export const installBroadcastChannelListener = () => {
|
||||
return;
|
||||
}
|
||||
|
||||
const client = event.source as Client;
|
||||
if (!client) {
|
||||
return;
|
||||
}
|
||||
|
||||
switch (event.data.type) {
|
||||
case 'prepare': {
|
||||
handlePrepareRequest(broadcast, url, requestId);
|
||||
handlePrepareRequest(client, url, requestId);
|
||||
break;
|
||||
}
|
||||
|
||||
case 'cancel': {
|
||||
handleCancelRequest(broadcast, url, requestId);
|
||||
handleCancelRequest(client, url, requestId);
|
||||
break;
|
||||
}
|
||||
}
|
||||
};
|
||||
});
|
||||
};
|
||||
Reference in New Issue
Block a user