@arcgis/core
Version:
ArcGIS Maps SDK for JavaScript: A complete 2D and 3D mapping and data visualization API
6 lines (5 loc) • 5.89 kB
JavaScript
/*
All material copyright ESRI, All Rights Reserved, unless otherwise specified.
See https://js.arcgis.com/4.33/esri/copyright.txt for details.
*/
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 a,isAbortError as h}from"../promiseUtils.js";import{createInvokeProxy as c}from"./InvokeHandler.js";import{registry as p}from"./registry.js";import{MessageType as _,portClosedErrorName as u,newJobId as d,receiveMessage as b,toInvokeError as m,postMessage 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:I,ON:J}=_,w=2;class O{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===_.ABORT?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 E{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 E(t.port1,{channel:t,client:o,schedule:s});return"object"==typeof o&&"remoteClient"in o&&(o.remoteClient=n),E.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 O((e=>this._onInvokeMessage(e))),this._client=s.client,this._onMessage=this._onMessage.bind(this),this._channel=s.channel,this._schedule=s.schedule,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 a=o?.signal,h=o?.transferList;if(!this._port)return Promise.reject(new s(u,`Cannot call invoke('${e}'), port is closed`,{methodName:e,data:t}));const c=d();return new Promise(((s,o)=>{if(i(a))return this._processWork(),void o(r());const p=l(a,(()=>{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!=a},t,h)}))}createInvokeProxy(e){return c(this,e)}on(e,s){const t=new MessageChannel;function n(e){s(e.data)}return this._port.postMessage({type:_.ON,eventType:e,port:t.port2},[t.port2]),t.port1.addEventListener("message",n),t.port1.start(),o((()=>{t.port1.postMessage({type:_.CLOSE}),t.port1.close(),t.port1.removeEventListener("message",n)}))}jobAdded(){this._processWork()}openPort(){const e=new MessageChannel;return this._post({type:I,port:e.port2}),e.port1}_processWork(){if(this._outJobs.size>=w)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}_onMessage(e){null!=this._schedule?this._schedule((()=>this._processMessage(e,!0))):this._processMessage(e,!1)}_processMessage(e,s){const t=b(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 I:this._onOpenPortMessage(t);break;case J: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&&E.clients.get(this)===e&&e.destroy(),E.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:m(_)})}a(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),h(e)||this._post({type:j,jobId:t,error:m(e||{message:`Error encountered at method ${s}`})}))}))):this._post({type:j,jobId:t},l)}_onOpenPortMessage(e){new E(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=b(e);t?.type===_.CLOSE&&(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 g(this._port,e,s,t)}}export{E as default};