Replies: 5 comments 8 replies
-
|
Hi @urugator @mweststrate Sorry for bothering you. I'd like to hear your opinion on that. Mobx already have flowResult, why not add one more zero-runtime helper? Even in the documentation example there is an issue: The variable |
Beta Was this translation helpful? Give feedback.
-
|
@kubk Found my way here too because I can't get the TS version of Flow's to the compile. I've been staring at the example for awhile now. I was keen to utilize the "cancel" method of flows, but its lot looking achievable. |
Beta Was this translation helpful? Give feedback.
-
|
A bit improved solution with a handy type alias, which I've been using in my projects. export type FlowGenerator<TReturn = void> = Generator<Promise<void>, TReturn, void>;
export function toFlowGeneratorFunction<TArgs extends unknown[], TReturn = void>(
fn: (...args: TArgs) => Promise<TReturn> | TReturn
): (...args: TArgs) => FlowGenerator<TReturn> {
return function* flowGeneratorFunction(...args: TArgs): FlowGenerator<TReturn> {
let value: TReturn = undefined as TReturn;
yield Promise.resolve(fn(...args)).then(result => {
value = result;
});
return value;
};
}I don't know when we could have something like this in Mobx or Mobx-utils, so I have published it as a package |
Beta Was this translation helpful? Give feedback.
-
|
I'm share my solution, or pattern. I'm using @flow.bound
private async *loadFAQList() {
try {
this.isLoading = true;
const faqList = await this.store.api.faq.fetchAll();
yield;
this.faqList = faqList;
} catch (e) {
yield;
console.error(e);
} finally {
this.isLoading = false;
}
}And some hacks. I can call async mobx flow actions, and await typed result. declare global {
// we can extend built-in AsyncGenerator 👀
// now, all async generators are promises also. No flowResult needs.
interface AsyncGenerator<T = unknown, TReturn = any, TNext = unknown> extends Promise<TReturn> {}
}Then we can do this: class Store {
// return type should be omitted
// return type infer as `AsyncGenerator<never, number[], unknown>` (and `Promise<number[]>` 😁)
@flow.bound async *method() {
return [1,2,3]
}
}
const example = async () => {
const store = new Store();
// return number[] (works!)
const result1 = await store.method()
// explicit type (works!)
const result2: number[] = await store.method()
// @ts-expect-error: Type number[] is not assignable to type string[]
const result3: string[] = await store.method();
}This is not perfect solution, types can still broken, in rare cases. But it works, as long as you follow the pattern. |
Beta Was this translation helpful? Give feedback.
-
|
I've found my way to this thread while trying to find a good way to use MobX via async patterns. The main issue of everything after an
EDIT: doesn't wok 😞 - I'll keep the example+yapping for context though class MobXPromise<T> extends Promise<T> {
then<TResult1 = T, TResult2 = never>(
onfulfilled?: ((value: T) => TResult1 | PromiseLike<TResult1>) | null | undefined,
onrejected?: ((reason: any) => TResult2 | PromiseLike<TResult2>) | null | undefined
): Promise<TResult1 | TResult2> {
return super.then(
onfulfilled ? ((value) => runInAction(() => onfulfilled(value))) : undefined,
onrejected ? ((reason) => runInAction(() => onrejected(reason))) : undefined,
);
}
catch<TResult = never>(
onrejected?: ((reason: any) => TResult | PromiseLike<TResult>) | null | undefined
): Promise<T | TResult> {
return super.catch(
onrejected ? ((reason) => runInAction(() => onrejected(reason))) : undefined,
);
}
finally(onfinally?: (() => void) | null | undefined): Promise<T> {
return super.finally(
onfinally ? (() => runInAction(() => onfinally())) : undefined,
);
}
}
/**
* Wraps all continuations in a MobX action.
* This works, because `await` is just syntax sugar over thenables.
*/
function newMobXPromise<T>(promise: Promise<T>): MobXPromise<T> {
return new MobXPromise((resolve, reject) => promise.then(resolve).catch(reject));
}This is almost identical to the class MobXStore {
async fetchTheStuff() {
const a = await mobXPromise(api.fetchA());
this.state.a = a;
const b = await mobXPromise(api.fetchB(a));
this.state.b = b;
const c = await mobXPromise(api.fetchA(b));
this.sate.c = c;
}
}Types and functionality seem to work on my end, but do let me know if there are some pitfalls to be aware of |
Beta Was this translation helpful? Give feedback.

Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
-
Mobx
flowis great because it allows to write async flows without wrapping observable modifications withaction/runInAction. But for TypeScript users it doesn't work well, because TS looses generator types:const a = yield 1; // type 'any' instead of 'number'There is an open TS issue: microsoft/TypeScript#32523
In MST repo I've found a trick to get type inference working:
or if we want to avoid type casts:
It works with no issue:
But to make it working you should enable
downlevelIterationin your tsconfig.jsonSo here is my question - should we add this utility function to Mobx codebase too and describe it's usage in documentation? I believe that Mobx
flowis useless for TS users without this function. Ready to make a PR.We already have
flowResultfunction for TS users. So it's better to come up with a better name rather thanresult:)Beta Was this translation helpful? Give feedback.
All reactions