////////////////////////////////////////////////////////////////////// // CXFlow function CXFlow(px){ let moncb=null; function setMonitor(callback){ moncb=callback; }; const plist={}; function CXFlowNode(rec){ rec=(rec)?rec:{}; this.name=("name" in rec)?rec.name:null; this.cidx=("cidx" in rec)?rec.cidx:0; this.task=("task" in rec)?rec.task:null; this.prt=("prt" in rec)?rec.prt:null; this.next=("next" in rec)?rec.next:null; this.child=("child" in rec)?rec.child:null; }; CXFlowNode.prototype={ getName:function(){ return(this.name)?this.name:"?"; }, getPath:function(){ return (this.prt?(this.prt.getPath()+"/"+this.getName()):""); }, build:function(sx){ const prt=this; sx.forEach(function(item){ const rec={ prt:prt }; if(item.length>=2){ rec.cidx=item[0]; rec.name=item[1]; } const xfn=prt.add(rec); if(item.length>=3){ xfn.build(item[2]); } }); return prt; }, find:function(path){ let xfn0=this; if(path){ const pax=path.split("/"); let i=0; while(xfn0&&(ixfn.cidx){ xfn.next=this.child; this.child=xfn; }else{ let xfn0=this.child; while(xfn0.next){ if(xfn0.next.cidx>xfn.cidx){ const tmp=xfn0.next; xfn0.next=xfn; xfn.next=tmp; break; } xfn0=xfn0.next; } if(!xfn0.next){ xfn0.next=xfn; } } } return xfn; }, add:function(rec){ return this.addNode(new CXFlowNode(rec)); }, remove:function(){ if(this.prt){ if(this.prt.child==this){ this.prt.child=this.next; }else{ let xfn0=this.prt.child; while(xfn0){ if(xfn0.next==this){ xfn0.next=this.next; break; } xfn0=xfn0.next; } } this.next=null; this.prt=null; } return this; }, run0:async function(data){ if(moncb){moncb(this);} if(this.child){ let xfn=this.child; let ret; while(xfn){ //--------------------------------------------------- // # move to the next node // resolve(); //--------------------------------------------------- // # proceed to the parent's next node // resolve("@break"); //--------------------------------------------------- // # proceed to the ancestor's next node // resolve({result:"@break",target:xfn_ancestor}); //--------------------------------------------------- // # exit // resolve({result:"@break",target:null}); //--------------------------------------------------- // # restart from the parent's first child node // resolve("@loop"); //--------------------------------------------------- // # restart from the ancestor's first child node // resolve({result:"@loop",target:xfn_ancestor}); //--------------------------------------------------- // # add a promise to "mylist" // resolve({result:"@add",name:"mylist",promise:new Promise(...)}); //--------------------------------------------------- // # make a promise from "mylist" with condition // resolve({result:"@promise",name:"mylist",method:all/race/...}); //--------------------------------------------------- ret=await xfn.run0(data); let result; const b_object=(ret&&(ret.constructor===Object)); if(b_object&&("result" in ret)){ result=ret.result; }else{ result=ret; } if((result==="@break")||(result==="@loop")){ if(b_object&&("target" in ret)&&(ret.target!==this)){ return ret; }else{ if(result==="@break"){ break; }else{ xfn=this.child; } } }else{ if(result==="@add"){ if(!(ret.name in plist)){ plist[ret.name]=[]; } plist[ret.name].push(ret.promise); } if(result==="@promise"){ const ls=plist[ret.name]; let p; switch(ret.method){ case "all":p=Promise.all(ls);break; case "race":p=Promise.race(ls);break; } delete plist[ret.name]; await p; } xfn=xfn.next; } } }else if(this.task){ return this.task(data); } }, run:async function(data,callback){ const ret=await this.run0(data); if(callback){ const resp=(ret&& (ret.constructor===Object)&& ("result" in ret))? ret:{result:"OK"}; callback(resp); } } }; const root=new CXFlowNode({name:"root"}); function build(sx){ return root.build(sx); }; function find(path){ let xfn0=null; if(path){ if(path[0]=="/"){ xfn0=root.find(path.substring(1)); } }else{ xfn0=root; } return xfn0; }; function add(path,rec){ let xfn0=find(path); if(xfn0){ return xfn0.add(rec); }else{ return false; } }; return{ build, find, add, setMonitor, }; };