Uh oh!
There was an error while loading.Please reload this page.
- Notifications
You must be signed in to change notification settings - Fork1.3k
Uh oh!
There was an error while loading.Please reload this page.
Uh oh!
There was an error while loading.Please reload this page.
-
I'm trying to wait for several resources to be acquired before advancing my state machine from I thought that I should do this by making This nearly worked, except that when the invoked actor Setting import{assign,createMachine,fromPromise}from"xstate";classMediaRecorderStream{constructor(stream,options){// ...}pipeTo(target,options){console.debug("<debug: pipeTo OK>");}}exportdefaultcreateMachine({id:"TapeRecorder",initial:"inactive",states:{inactive:{on:{action_record:"recording",},},recording:{initial:"acquiring",invoke:[{src:"MediaRecorderStream",id:"mic",input:{query:{audio:true,video:false},mimeType:"audio/webm;codecs=opus",},onDone:{actions:assign({mic:({ event})=>event.output}),target:".acquiring.acquiring_mic.ready",reenter:false,},onError:{actions:({ event})=>console.error(event.error),target:".stopped"},},{src:"FileWritableStream",id:"file",input:{suggestedName:"out.weba",},onDone:{actions:assign({file:({ event})=>event.output}),target:".acquiring.acquiring_file.ready",reenter:false,},onError:{actions:({ event})=>console.error(event.error),target:".stopped"},},],states:{acquiring:{type:"parallel",states:{acquiring_mic:{initial:"acquiring",states:{acquiring:{},ready:{type:"final",},},},acquiring_file:{initial:"acquiring",states:{acquiring:{},ready:{type:"final",},},},},onDone:"recording",},recording:{initial:"recording",invoke:{src:"PipeTo",input:({ context})=>({source:context.mic,target:context.file,}),},states:{recording:{on:{action_pause:"paused",},},paused:{on:{action_resume:"recording",},},},},stopped:{type:"final",},},on:{action_stop:".stopped",},onDone:"inactive",},},},{actors:{PipeTo:fromPromise(({input:{ source, target, options}, signal})=>source.pipeTo(target,options)),FileWritableStream:fromPromise(({input:options, signal})=>(//window.showSaveFilePicker(options)//new Promise((_resolve, reject) => setTimeout(() => reject(new Error("<debug>")), 1500))navigator.storage.getDirectory().then((d)=>d.getFileHandle(options.suggestedName,{create:true}))).then((f)=>f.createWritable())),MediaRecorderStream:fromPromise(({input:{ query, options}, signal})=>navigator.mediaDevices.getUserMedia(query).then((stream)=>newMediaRecorderStream(stream,options))),},},); OK, on further study, I realized that a Promise actor isn't quite the correct tool here. But when using other actors, which don't "produce output" and have to be wired up with a custom import{assign,createMachine,fromCallback,fromPromise}from"xstate";classMediaRecorderStream{constructor(stream,options){// ...}pipeTo(target,options){console.debug("<debug: pipeTo OK>");}}exportdefaultcreateMachine({id:"TapeRecorder",initial:"inactive",states:{inactive:{on:{action_record:"recording",},},recording:{invoke:[{src:"MediaRecorderStream",id:"mic",input:{query:{audio:true,video:false},mimeType:"audio/webm;codecs=opus",fixme1:"nhpuv6",},onError:{actions:({ event})=>console.error(event.error),target:".stopped"},},{src:"FileWritableStream",id:"file",input:{options:{suggestedName:"out.weba"},fixme1:"xdj2de",},onError:{actions:({ event})=>console.error(event.error),target:".stopped"},},],initial:"acquiring",states:{acquiring:{type:"parallel",states:{acquiring_mic:{initial:"acquiring",states:{acquiring:{on:{ready_nhpuv6:{actions:assign({mic:({ event})=>event.output}),target:"ready",},},},ready:{type:"final",},},},acquiring_file:{initial:"acquiring",states:{acquiring:{on:{ready_xdj2de:{actions:assign({file:({ event})=>event.output}),target:"ready",},},},ready:{type:"final",},},},},onDone:"recording",},recording:{initial:"recording",invoke:{src:"PipeTo",input:({ context})=>({source:context.mic,target:context.file,}),},states:{recording:{on:{action_pause:"paused",},},paused:{on:{action_resume:"recording",},},},},stopped:{type:"final",},},on:{action_stop:".stopped",},onDone:"inactive",},},},{actors:{PipeTo:fromPromise(({input:{ source, target, options}, signal})=>source.pipeTo(target,options)),FileWritableStream:fromCallback(({ sendBack, receive,input:{ options, fixme1}, signal})=>{console.debug(signal);(//window.showSaveFilePicker(options)//new Promise((_resolve, reject) => setTimeout(() => reject(new Error("<debug>")), 1500))navigator.storage.getDirectory().then((d)=>d.getFileHandle(options.suggestedName,{create:true}))).then((f)=>f.createWritable()).then((w)=>sendBack({type:`ready_${fixme1}`,output:w}));return()=>{console.debug("<debug: cleanup file>");};}),MediaRecorderStream:fromCallback(({ sendBack, receive,input:{ query, options, fixme1}, signal})=>{navigator.mediaDevices.getUserMedia(query).then((media)=>sendBack({type:`ready_${fixme1}`,output:newMediaRecorderStream(media,options)}));return()=>{console.debug("<debug: cleanup media>");};}),},},); |
BetaWas this translation helpful?Give feedback.
All reactions
Replies: 2 comments 2 replies
Uh oh!
There was an error while loading.Please reload this page.
Uh oh!
There was an error while loading.Please reload this page.
-
Interim solution that isn't so unspeakably bad: have the actors brand the /** * resourceActor({ * acquire: async (options) => {...}, * release: async (resource) => {...}, * }) */functionresourceActor({ acquire, release}){returnfromCallback(({input:options, self, sendBack})=>{varresource_=Promise.resolve(acquire(options));resource_.then((result)=>voidsendBack({type:"ready",output:result,_fixme_xstate_5335:self.id}),(error)=>voidsendBack({type:"error", error}));return()=>voidresource_.then((result)=>release(result));});}/** * on: { * ready: { * guard: fromActor("some_invoked_actor"), * actions: assign({ some_resource: ({ event }) => event.output }), * target: "ready", * }, * }, */functionfromActor(id){return({ event})=>(event._fixme_xstate_5335===id);} |
BetaWas this translation helpful?Give feedback.
All reactions
-
Would using |
BetaWas this translation helpful?Give feedback.
All reactions
-
I think not; I actually discovered that Promise actorscan't be used for resource management like I thought, since they're deemed to be terminated after settled. I'm currently using a template for callback actorsas detailed here. |
BetaWas this translation helpful?Give feedback.
All reactions
-
I've got a more fully worked examplehere, showing a pretty minimal "real-world" use-case of resource acquisition. I just can't see how to accomplish this without that annoying extra "tag" on the event. |
BetaWas this translation helpful?Give feedback.