Skip to content

Instantly share code, notes, and snippets.

@lorefnon
Created May 25, 2020 06:30
Show Gist options
  • Save lorefnon/aa123570a06b7e3cabf4621209c8d10a to your computer and use it in GitHub Desktop.
Save lorefnon/aa123570a06b7e3cabf4621209c8d10a to your computer and use it in GitHub Desktop.
MST flowPipe with async/await
// Ref: https://github.com/mobxjs/mobx-state-tree/issues/1516
import { flow } from "mobx-state-tree";
interface PromiseWrapper<T> {
_type: "PromiseWrapper";
resolve: (result: T) => void;
reject: (err: any) => void;
originalPromise: Promise<T>;
wrappedPromise: Promise<T>;
}
interface MutationWrapper {
_type: "MutationWrapper";
mutate: () => void;
resolve: () => void;
reject: (err: any) => void;
wrappedPromise: Promise<void>;
}
type Deferred<T> = PromiseWrapper<T> | MutationWrapper;
const wrapPromise = <T>(originalPromise: Promise<T>): PromiseWrapper<T> => {
let resolve: undefined | ((result: T) => void);
let reject: undefined | ((err: any) => void);
const wrappedPromise = new Promise<T>((_resolve, _reject) => {
resolve = _resolve;
reject = _reject;
});
return {
_type: "PromiseWrapper",
resolve: resolve!,
reject: reject!,
originalPromise,
wrappedPromise
};
};
const wrapMutation = (mutate: () => void): MutationWrapper => {
let resolve: undefined | (() => void);
let reject: undefined | ((err: any) => void);
const wrappedPromise = new Promise<void>((_resolve, _reject) => {
resolve = _resolve;
reject = _reject;
});
return {
_type: "MutationWrapper",
resolve: resolve!,
reject: reject!,
mutate,
wrappedPromise
};
};
interface Interceptors {
interceptPromise: <T>(p: Promise<T>) => Promise<T>;
mutate: (fn: () => void) => Promise<void>;
}
export function flowPipe(asyncFn: (i: Interceptors) => Promise<any>) {
return flow(function*() {
const deferreds: Deferred<any>[] = [];
let didEnd = false;
let thrown: any;
asyncFn({
interceptPromise: <T>(p: Promise<T>): Promise<T> => {
const wrapper = wrapPromise(p);
deferreds.push(wrapper);
return wrapper.wrappedPromise;
},
mutate: (fn: () => void) => {
const wrapper = wrapMutation(fn);
deferreds.push(wrapper);
return wrapper.wrappedPromise;
}
})
.catch(e => {
console.error(e);
thrown = e;
})
.finally(() => {
didEnd = true;
});
while (true) {
yield new Promise(resolve => setImmediate(resolve));
let deferred: Deferred<any> | undefined;
// eslint-disable-next-line
while ((deferred = deferreds.shift())) {
switch (deferred._type) {
case "PromiseWrapper":
try {
const result = yield deferred.originalPromise;
deferred.resolve(result);
} catch (e) {
deferred.reject(e);
}
break;
case "MutationWrapper":
try {
deferred.mutate();
deferred.resolve();
} catch (e) {
deferred.reject(e);
}
}
}
if (didEnd) break;
}
if (thrown) {
throw thrown;
}
});
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment