@arcgis/core
Version:
ArcGIS Maps SDK for JavaScript: A complete 2D and 3D mapping and data visualization API
3 lines (2 loc) • 5.97 kB
JavaScript
/* COPYRIGHT Esri - https://js.arcgis.com/5.0.8/LICENSE.txt */
import{fullVersion as e}from"../../kernel.js";import s from"../Error.js";import{on as t}from"../events.js";import{makeHandle as o}from"../handleUtils.js";import{removeMaybe as n}from"../maybe.js";import{isAborted as i,createAbortError as r,onAbort as l,isPromiseLike as h,isAbortError as a}from"../promiseUtils.js";import{createInvokeProxy as c}from"./InvokeHandler.js";import{registry as p}from"./registry.js";import{MessageType as _,portClosedErrorName as u,receiveMessage as d,toInvokeError as b,postMessage as m,newJobId as g}from"./utils.js";import{commitHash as f,buildDate as v}from"../../support/revision.js";const{CLOSE:M,ABORT:k,INVOKE:y,RESPONSE:j,OPEN_PORT:J,ON:I}=_;class w{constructor(e){this._invoke=e,this._timer=null,this._cancelledJobIds=new Set,this._invokeMessages=[],this._timer=null,this._process=this._process.bind(this)}push(e){e.type===k?this._cancelledJobIds.add(e.jobId):(this._invokeMessages.push(e),null===this._timer&&(this._timer=setTimeout(this._process,0)))}clear(){this._invokeMessages.length=0,this._cancelledJobIds.clear(),this._timer=null}_process(){this._timer=null;for(const e of this._invokeMessages)this._cancelledJobIds.has(e.jobId)||this._invoke(e);this._cancelledJobIds.clear(),this._invokeMessages.length=0}}class O{static{this.kernelInfo={buildDate:v,fullVersion:e,revision:f}}static{this.clients=new Map}static connect(e,s){const t=new MessageChannel;let o;o="function"==typeof e?new e:"default"in e&&"function"==typeof e.default?new e.default:e;const n=new O(t.port1,{channel:t,client:o,schedule:s});return"object"==typeof o&&"remoteClient"in o&&(o.remoteClient=n),O.clients.set(n,o),t.port2}static loadWorker(e){const s=p[e];return s?s():Promise.resolve(null)}constructor(e,s,t,o){this._port=e,this._jobQueue=t,this._lowPriorityJobQueue=o,this._outJobs=new Map,this._inJobs=new Map,this._invokeQueue=new w(e=>this._onInvokeMessage(e)),this._client=s.client,this._onMessage=this._onMessage.bind(this),this._channel=s.channel,this._schedule=s.schedule,this._maxNumberOfConcurrentJobs=s.maxNumberOfConcurrentJobs??2,this._port.addEventListener("message",this._onMessage),this._port.start()}close(){this._post({type:M}),this._close()}isBusy(){return this._outJobs.size>0}invoke(e,s,t){return this.apply(e,[s],t)}apply(e,t,o){const h=o?.signal,a=o?.transferList;if(!this._port)return Promise.reject(new s(u,`Cannot call invoke('${e}'), port is closed`,{methodName:e,data:t}));const c=g();return new Promise((s,o)=>{if(i(h))return this._processWork(),void o(r());const p=l(h,()=>{const e=this._outJobs.get(c);e&&(this._outJobs.delete(c),this._processWork(),n(e.abortHandle),this._post({type:k,jobId:c}),o(r()))}),_={resolve:s,reject:o,abortHandle:p,debugInfo:e};this._outJobs.set(c,_),this._post({type:y,jobId:c,methodName:e,abortable:null!=h},t,a)})}createInvokeProxy(e){return c(this,e)}on(e,s){const t=new MessageChannel;function n(e){s(e.data)}return this._port.postMessage({type:I,eventType:e,port:t.port2},[t.port2]),t.port1.addEventListener("message",n),t.port1.start(),o(()=>{t.port1.postMessage({type:M}),t.port1.close(),t.port1.removeEventListener("message",n)})}jobAdded(){this._processWork()}openPort(){const e=new MessageChannel;return this._post({type:J,port:e.port2}),e.port1}_processWork(){if(this._outJobs.size>=this._maxNumberOfConcurrentJobs)return;const e=this._jobQueue?.pop()??this._lowPriorityJobQueue?.pop();if(!e)return;const{methodName:s,data:t,invokeOptions:o,resolver:n}=e;this.apply(s,t,o).then(e=>n.resolve(e)).catch(e=>n.reject(e))}_close(){this._channel&&(this._channel=void 0),this._port.removeEventListener("message",this._onMessage),this._port.close(),this._outJobs.forEach(e=>{n(e.abortHandle),e.reject(r(`Worker closing, aborting job calling '${e.debugInfo}'`))}),this._inJobs.clear(),this._outJobs.clear(),this._invokeQueue.clear(),this._port=null,this._client=null,this._schedule=null,this._onMessage=null,this._channel=null,this._jobQueue=void 0,this._invokeQueue=void 0,this._lowPriorityJobQueue=void 0}_onMessage(e){null!=this._schedule?this._schedule(()=>this._processMessage(e,!0)):this._processMessage(e,!1)}_processMessage(e,s){const t=d(e);if(t)switch(t.type){case j:this._onResponseMessage(t);break;case y:s?this._onInvokeMessage(t):this._invokeQueue.push(t);break;case k:this._onAbortMessage(t);break;case M:this._onCloseMessage();break;case J:this._onOpenPortMessage(t);break;case I:this._onOnMessage(t)}}_onAbortMessage(e){const s=this._inJobs,t=e.jobId,o=s.get(t);this._invokeQueue.push(e),o&&(o.controller&&o.controller.abort(),s.delete(t))}_onCloseMessage(){const e=this._client;this._close(),e&&"destroy"in e&&O.clients.get(this)===e&&e.destroy(),O.clients.delete(this),e?.remoteClient&&(e.remoteClient=null)}_onInvokeMessage(e){const{methodName:s,jobId:t,data:o=[],abortable:n}=e,i=n?new AbortController:null,r=this._inJobs;let l,c=this._client,p=c[s];try{if(!p&&s&&s.includes(".")){const e=s.split(".");for(let s=0;s<e.length-1;s++)c=c[e[s]],p=c[e[s+1]]}if("function"!=typeof p)throw new TypeError(`${s} is not a function`);o.push({client:this,signal:i?i.signal:null}),l=p.apply(c,o)}catch(_){return void this._post({type:j,jobId:t,error:b(_)})}h(l)?(r.set(t,{controller:i,promise:l}),l.then(e=>{r.has(t)&&(r.delete(t),this._post({type:j,jobId:t},e))},e=>{r.has(t)&&(r.delete(t),a(e)||this._post({type:j,jobId:t,error:b(e||{message:`Error encountered at method ${s}`})}))})):this._post({type:j,jobId:t},l)}_onOpenPortMessage(e){new O(e.port,{client:this._client})}_onOnMessage(e){const{port:s}=e,o=this._client.on(e.eventType,e=>{s.postMessage(e)}),n=t(e.port,"message",e=>{const t=d(e);t?.type===M&&(n.remove(),o.remove(),s.close())})}_onResponseMessage(e){const{jobId:t,error:o,data:i}=e,r=this._outJobs;if(!r.has(t))return;const l=r.get(t);r.delete(t),this._processWork(),n(l.abortHandle),o?l.reject(s.fromJSON(JSON.parse(o))):l.resolve(i)}_post(e,s,t){return m(this._port,e,s,t)}}export{O as default};