@@ -23,20 +23,26 @@ export interface TileRequest {
2323 mapBounds : number [ ] ;
2424}
2525
26- export type TileResponse = {
26+ export type TileResponse = ImageBitmap | ArrayBuffer ;
27+ export type TilePromise = Promise < TileResponse > ;
28+
29+ export type WorkerResponse = {
2730 type : 'returnImage' | 'returnArrayBuffer' ;
28- tile : ImageBitmap ;
31+ tile : TileResponse ;
2932 key : string ;
3033} ;
3134
3235export class WorkerPool {
3336 private workers : Worker [ ] = [ ] ;
3437 private nextWorker = 0 ;
3538 /** Stores pending tile requests by key to avoid duplicate requests for the same tile */
36- private pendingTiles = new Map < string , Promise < ImageBitmap > > ( ) ;
39+ private pendingTiles = new Map < string , TilePromise > ( ) ;
3740
38- /** Stores resolve functions for pending promises, used to fulfill promises when worker responses arrive */
39- private resolvers = new Map < string , ( tile : ImageBitmap ) => void > ( ) ;
41+ /**
42+ * Stores an array of resolve functions for each pending key.
43+ * This allows for multiple subscribers for the same tile key.
44+ */
45+ private resolvers = new Map < string , Array < ( tile : TileResponse ) => void > > ( ) ;
4046
4147 constructor ( ) {
4248 if ( typeof window === 'undefined' || typeof Worker === 'undefined' ) {
@@ -47,16 +53,35 @@ export class WorkerPool {
4753 for ( let i = 0 ; i < workerCount ; i ++ ) {
4854 const worker = new TileWorker ( ) ;
4955 worker . onmessage = ( message : MessageEvent ) => this . handleMessage ( message ) ;
56+ worker . onerror = ( error : ErrorEvent ) => this . handleError ( error ) ;
5057 this . workers . push ( worker ) ;
5158 }
5259 }
5360
5461 private handleMessage ( message : MessageEvent ) : void {
55- const data = message . data as TileResponse ;
62+ const data = message . data as WorkerResponse ;
5663 if ( data . type . startsWith ( 'return' ) ) {
57- const resolve = this . resolvers . get ( data . key ) ;
58- if ( resolve ) {
59- resolve ( data . tile ) ;
64+ const resolveFns = this . resolvers . get ( data . key ) ;
65+
66+ if ( resolveFns && resolveFns . length > 0 ) {
67+ const originalTile = data . tile ;
68+
69+ // The first subscriber can receive the original (transferred) buffer.
70+ const firstResolver = resolveFns . shift ( ) ! ;
71+ firstResolver ( originalTile ) ;
72+
73+ // All other subscribers must receive a clone.
74+ resolveFns . forEach ( ( resolve ) => {
75+ if ( originalTile instanceof ArrayBuffer ) {
76+ // Create a copy for each subsequent subscriber.
77+ resolve ( originalTile . slice ( 0 ) ) ;
78+ } else {
79+ // ImageBitmaps are safe to share without cloning.
80+ resolve ( originalTile ) ;
81+ }
82+ } ) ;
83+
84+ // Clean up now that all promises for this key are resolved.
6085 this . resolvers . delete ( data . key ) ;
6186 this . pendingTiles . delete ( data . key ) ;
6287 } else {
@@ -65,6 +90,11 @@ export class WorkerPool {
6590 }
6691 }
6792
93+ private handleError ( error : ErrorEvent ) : void {
94+ // Simplified error handler: just log for now
95+ console . error ( 'Error in worker:' , error . message , error ) ;
96+ }
97+
6898 public getNextWorker ( ) : Worker | undefined {
6999 if ( this . workers . length === 0 ) return undefined ;
70100
@@ -73,22 +103,29 @@ export class WorkerPool {
73103 return worker ;
74104 }
75105
76- public requestTile ( request : TileRequest ) : Promise < ImageBitmap > {
77- const existingPromise = this . pendingTiles . get ( request . key ) ;
78- if ( existingPromise ) {
79- return existingPromise ;
106+ public requestTile ( request : TileRequest ) : TilePromise {
107+ // If a request for this key is already in flight...
108+ if ( this . pendingTiles . has ( request . key ) ) {
109+ // ...create a new promise and add its resolver to the list for this key.
110+ return new Promise < TileResponse > ( ( resolve ) => {
111+ this . resolvers . get ( request . key ) ! . push ( resolve ) ;
112+ } ) ;
80113 }
81114
115+ // This is the first request for this key.
82116 const worker = this . getNextWorker ( ) ;
83117 if ( ! worker ) {
84118 return Promise . reject ( new Error ( 'No workers available (likely running in SSR)' ) ) ;
85119 }
86120
87- const promise = new Promise < ImageBitmap > ( ( resolve ) => {
88- this . resolvers . set ( request . key , resolve ) ;
121+ // Create the promise and store its resolver in a new array.
122+ const promise = new Promise < TileResponse > ( ( resolve ) => {
123+ this . resolvers . set ( request . key , [ resolve ] ) ;
89124 } ) ;
90125
126+ // Store the master promise to indicate a request is in-flight.
91127 this . pendingTiles . set ( request . key , promise ) ;
128+
92129 worker . postMessage ( request ) ;
93130
94131 return promise ;
0 commit comments