/async-flow.ts Secret
Created
October 12, 2021 02:33
use async/await to build mst-flow but preserves action context
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { flow, onAction, types } from "mobx-state-tree"; | |
type Work = (...args) => void | undefined; | |
class Workflow { | |
private queue = []; | |
private _resolve; | |
private resolved; | |
private _disposed = false; | |
get disposed() { | |
return this._disposed; | |
} | |
private release() { | |
if (this.resolved) { | |
this._resolve?.(); | |
} else { | |
this.resolved = new Promise<unknown>(resolve => this._resolve = resolve); | |
this._resolve(); | |
} | |
} | |
private async lock() { | |
// wait existing lock | |
if (this.resolved) { | |
await this.resolved; | |
} | |
// set new lock | |
this.resolved = new Promise<unknown>(resolve => this._resolve = resolve); | |
return this.resolved; | |
} | |
yield(work: Work) { | |
const result = new Promise(resolve => { | |
this.queue.push([work, resolve]); | |
}); | |
this.release(); | |
return result; | |
} | |
async run() { | |
await this.lock(); | |
const task = this.queue.pop(); | |
return () => { | |
if (task) { | |
const [work, resolve] = task; | |
work?.(); | |
resolve?.(); | |
} | |
} | |
} | |
dispose() { | |
this._disposed = true; | |
this.release(); | |
} | |
} | |
type MstAction = (work: Work) => Promise<void>; | |
const asyncFlow = <TA extends any[], T>(workflow: (mstAction: MstAction) => (...args: TA) => Promise<T>) => { | |
const wf = new Workflow(); | |
const mstAction: MstAction = async (work: Work) => { | |
await wf.yield(work); | |
}; | |
return flow(function* (...args: TA) { | |
const result = Promise.resolve().then(() => workflow(mstAction)(...args).finally(() => { | |
wf.dispose(); | |
})); | |
while (!wf.disposed) { | |
(yield wf.run())(); | |
} | |
return yield result; | |
}); | |
} | |
const foo = types.model('Foo', { | |
foo: '' | |
}).actions(self => { | |
return { | |
asyncOp: asyncFlow(mstAction => async (bar: string) => { | |
await mstAction(() => self.foo = '123') | |
console.log('foo: ' + self.foo); | |
await mstAction(() => self.foo = self.foo + '123') | |
return 3; | |
}) | |
} | |
}); | |
(async () => { | |
try { | |
let tree = foo.create(); | |
onAction(tree, c => console.log(c.name, c.path)); | |
await tree.asyncOp('foo').then((x) => console.log('finished ' + x)) | |
} catch (e) { | |
console.log(e) | |
} | |
})() | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment