|  |  |  | /* global Worker, window, setTimeout */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | const WORKER_TIMEOUT = 60 * 1000; // one minute
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | class TimedOutError extends Error { | 
					
						
							|  |  |  |   constructor(message) { | 
					
						
							|  |  |  |     super(message); | 
					
						
							|  |  |  |     this.name = this.constructor.name; | 
					
						
							|  |  |  |     if (typeof Error.captureStackTrace === 'function') { | 
					
						
							|  |  |  |       Error.captureStackTrace(this, this.constructor); | 
					
						
							|  |  |  |     } else { | 
					
						
							|  |  |  |       this.stack = new Error(message).stack; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | class WorkerInterface { | 
					
						
							|  |  |  |   constructor(path, timeout = WORKER_TIMEOUT) { | 
					
						
							|  |  |  |     this._utilWorker = new Worker(path); | 
					
						
							|  |  |  |     this.timeout = timeout; | 
					
						
							|  |  |  |     this._jobs = Object.create(null); | 
					
						
							|  |  |  |     this._DEBUG = false; | 
					
						
							|  |  |  |     this._jobCounter = 0; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     this._utilWorker.onmessage = e => { | 
					
						
							|  |  |  |       const [jobId, errorForDisplay, result] = e.data; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       const job = this._getJob(jobId); | 
					
						
							|  |  |  |       if (!job) { | 
					
						
							|  |  |  |         throw new Error( | 
					
						
							|  |  |  |           `Received worker reply to job ${jobId}, but did not have it in our registry!` | 
					
						
							|  |  |  |         ); | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       const { resolve, reject, fnName } = job; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       if (errorForDisplay) { | 
					
						
							|  |  |  |         return reject( | 
					
						
							|  |  |  |           new Error(`Error received from worker job ${jobId} (${fnName}): ${errorForDisplay}`) | 
					
						
							|  |  |  |         ); | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       return resolve(result); | 
					
						
							|  |  |  |     }; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   _makeJob(fnName) { | 
					
						
							|  |  |  |     this._jobCounter += 1; | 
					
						
							|  |  |  |     const id = this._jobCounter; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (this._DEBUG) { | 
					
						
							|  |  |  |       window.log.info(`Worker job ${id} (${fnName}) started`); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     this._jobs[id] = { | 
					
						
							|  |  |  |       fnName, | 
					
						
							|  |  |  |       start: Date.now(), | 
					
						
							|  |  |  |     }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     return id; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   _updateJob(id, data) { | 
					
						
							|  |  |  |     const { resolve, reject } = data; | 
					
						
							|  |  |  |     const { fnName, start } = this._jobs[id]; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     this._jobs[id] = { | 
					
						
							|  |  |  |       ...this._jobs[id], | 
					
						
							|  |  |  |       ...data, | 
					
						
							|  |  |  |       resolve: value => { | 
					
						
							|  |  |  |         this._removeJob(id); | 
					
						
							|  |  |  |         const end = Date.now(); | 
					
						
							|  |  |  |         if (this._DEBUG) { | 
					
						
							|  |  |  |           window.log.info(`Worker job ${id} (${fnName}) succeeded in ${end - start}ms`); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         return resolve(value); | 
					
						
							|  |  |  |       }, | 
					
						
							|  |  |  |       reject: error => { | 
					
						
							|  |  |  |         this._removeJob(id); | 
					
						
							|  |  |  |         const end = Date.now(); | 
					
						
							|  |  |  |         window.log.info(`Worker job ${id} (${fnName}) failed in ${end - start}ms`); | 
					
						
							|  |  |  |         return reject(error); | 
					
						
							|  |  |  |       }, | 
					
						
							|  |  |  |     }; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   _removeJob(id) { | 
					
						
							|  |  |  |     if (this._DEBUG) { | 
					
						
							|  |  |  |       this._jobs[id].complete = true; | 
					
						
							|  |  |  |     } else { | 
					
						
							|  |  |  |       delete this._jobs[id]; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   _getJob(id) { | 
					
						
							|  |  |  |     return this._jobs[id]; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   callWorker(fnName, ...args) { | 
					
						
							|  |  |  |     const jobId = this._makeJob(fnName); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     return new Promise((resolve, reject) => { | 
					
						
							|  |  |  |       this._utilWorker.postMessage([jobId, fnName, ...args]); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       this._updateJob(jobId, { | 
					
						
							|  |  |  |         resolve, | 
					
						
							|  |  |  |         reject, | 
					
						
							|  |  |  |         args: this._DEBUG ? args : null, | 
					
						
							|  |  |  |       }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       setTimeout( | 
					
						
							|  |  |  |         () => reject(new TimedOutError(`Worker job ${jobId} (${fnName}) timed out`)), | 
					
						
							|  |  |  |         this.timeout | 
					
						
							|  |  |  |       ); | 
					
						
							|  |  |  |     }); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | module.exports = WorkerInterface; |