_Feature request_
A typesafe version of flow
Is your feature request related to a problem? Please describe.
Typescript has an issue with flow in multi-step functions because yield returns any type which is no good.
Describe the solution you'd like
Perhaps a different alternative is to use a pipe like solution borrowed from FP or RXJS
Additional context
Just to get the idea across take a look at this:
export const flowPipe = <T0, T1, T2, T3>(
step0: (arg: T0) => Promise<T1>,
step1: (arg: T1) => Promise<T2>,
step2: (arg: T2) => Promise<T3>
) =>
flow<T3, [T0]>(function*(args: T0) {
const arg1 = yield step0(args);
const arg2 = yield step1(arg1);
const arg3 = yield step2(arg2);
return arg3;
});
This could then be used like:
.actions(self => ({
myAsyncAction: flowPipe(
(input: string) => callMyApi(input),
(output) => self.doSomethingElseAsyncWithTheOutput(output),
() => self.doAnotherAsyncThing()
),
}));
Now obviously we could make it infinate number of steps rather than limit it to 3. The type signature we could borrow from RXJS's pipe: https://github.com/ReactiveX/rxjs/blob/master/src/internal/Observable.ts#L309-L319
Thoughts?
Are you willing to (attempt) a PR?
This seems to work tho I havent fully tested it:
import { flow } from "mobx-state-tree";
import { FlowReturn } from "mobx-state-tree/dist/core/flow";
type StepFn<TInput, TOutput> = (input: TInput) => Promise<TOutput> | TOutput;
type FlowFn<TInput, TOutput> = (input: TInput) => Promise<FlowReturn<TOutput>>;
export function flowPipe<I, T0>(step0: StepFn<I, T0>): FlowFn<I, T0>;
export function flowPipe<I, T0, T1>(step0: StepFn<I, T0>, step1: StepFn<T0, T1>): FlowFn<I, T1>;
export function flowPipe<I, T0, T1, T2>(
step0: StepFn<I, T0>,
step1: StepFn<T0, T1>,
step2: StepFn<T1, T2>
): FlowFn<I, T2>;
export function flowPipe<I, T0, T1, T2, T3>(
step0: StepFn<I, T0>,
step1: StepFn<T0, T1>,
step2: StepFn<T1, T2>,
step3: StepFn<T2, T3>
): FlowFn<I, T3>;
export function flowPipe(...steps: StepFn<any, any>[]) {
return flow(function*(input: any) {
for (let step of steps) {
const result = step(input);
input = yield isPromise(result) ? result : Promise.resolve(result);
}
return input;
});
}
const isPromise = (o: any) => o != undefined && o.hasOwnProperty("then");
usage:
testPipe: flowPipe(
(someInput: {}) => {
return Promise.resolve(true);
},
val => "sdfsdf",
val => wait(2000),
val => Promise.resolve(123)
),
Okay I find this method superior to any of the other async methods so I decided to formalise it by turning it into a libary. Keen to get your thoughts on it anyone: https://github.com/mikecann/mst-flow-pipe
There is a small issue with some of the typings which I have mentioned at the bottom fo that readme, please let me know if you know how to solve.
If you feel this is valuable enough to be pulled into MST let me know otherwise ill just close this issue and hope others find the library organically.
Thanks to the awesome contrib from @lorefnon we are now at v2 and it works without any issues now :) https://github.com/mikecann/mst-flow-pipe
Thanks for your great work and your effort.
Do you think there might be a solution that would allow us to use yield or await instead of having to chain .then?
@timbicker just use "flow" if you want to use yield :)
Do you think there might be a solution that would allow us to use yield or await instead of having to chain .then?
It is possible, but the resulting API is not very ergonomic: Sample Sandbox
const Store = t
.model({
items: t.optional(t.array(Item), [])
})
.actions(self => ({
// Use flowPipe to define an asynchronous action
fetchMore: flowPipe(async ({ interceptPromise, mutate }) => {
// Any promise needs to be wrapped in interceptPromise
const names = await interceptPromise(fetchMoreItems()); // type of names is correctly inferred
// Any mutations to state must be wrapped in mutate
await mutate(() => {
self.items.push(...names.map(name => ({ name })));
});
})
}));
The implementation also slightly delays the resolution of promises due to use of setImmediate.
Hence I think the implementation in https://github.com/mikecann/mst-flow-pipe is better.
Most helpful comment
Thanks to the awesome contrib from @lorefnon we are now at v2 and it works without any issues now :) https://github.com/mikecann/mst-flow-pipe