Skip to content

Instantly share code, notes, and snippets.

@lorefnon
Created May 25, 2020 06:30

Revisions

  1. lorefnon revised this gist May 25, 2020. 1 changed file with 1 addition and 0 deletions.
    1 change: 1 addition & 0 deletions flow-pipe.ts
    Original file line number Diff line number Diff line change
    @@ -1,3 +1,4 @@
    // Ref: https://github.com/mobxjs/mobx-state-tree/issues/1516

    import { flow } from "mobx-state-tree";

  2. lorefnon created this gist May 25, 2020.
    112 changes: 112 additions & 0 deletions flow-pipe.ts
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,112 @@

    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;
    }
    });
    }