From 422aac227174a67d2a8716057173c4a3d0a5e999 Mon Sep 17 00:00:00 2001 From: Fabien JUIF Date: Tue, 31 Oct 2017 15:34:10 +0100 Subject: [PATCH 1/2] :sparkles: auto flow --- packages/mobx-state-tree/src/core/flow.ts | 18 ++++++++++++++---- packages/mobx-state-tree/src/core/process.ts | 6 +++--- .../src/types/complex-types/model.ts | 10 +++++++++- packages/mobx-state-tree/src/utils.ts | 5 +++++ 4 files changed, 31 insertions(+), 8 deletions(-) diff --git a/packages/mobx-state-tree/src/core/flow.ts b/packages/mobx-state-tree/src/core/flow.ts index 6d0c59248..e6cd6c0fc 100644 --- a/packages/mobx-state-tree/src/core/flow.ts +++ b/packages/mobx-state-tree/src/core/flow.ts @@ -1,26 +1,36 @@ // based on: https://github.com/mobxjs/mobx-utils/blob/master/src/async-action.ts -export function flow(generator: () => IterableIterator): () => Promise -export function flow(generator: (a1: A1) => IterableIterator): (a1: A1) => Promise // Ideally we want to have R instead of Any, but cannot specify R without specifying A1 etc... 'any' as result is better then not specifying request args +export function flow(name: string, generator: () => IterableIterator): () => Promise +export function flow( + name: string, + generator: (a1: A1) => IterableIterator +): (a1: A1) => Promise // Ideally we want to have R instead of Any, but cannot specify R without specifying A1 etc... 'any' as result is better then not specifying request args export function flow( + name: string, generator: (a1: A1, a2: A2) => IterableIterator ): (a1: A1, a2: A2) => Promise export function flow( + name: string, generator: (a1: A1, a2: A2, a3: A3) => IterableIterator ): (a1: A1, a2: A2, a3: A3) => Promise export function flow( + name: string, generator: (a1: A1, a2: A2, a3: A3, a4: A4) => IterableIterator ): (a1: A1, a2: A2, a3: A3, a4: A4) => Promise export function flow( + name: string, generator: (a1: A1, a2: A2, a3: A3, a4: A4, a5: A5) => IterableIterator ): (a1: A1, a2: A2, a3: A3, a4: A4, a5: A5) => Promise export function flow( + name: string, generator: (a1: A1, a2: A2, a3: A3, a4: A4, a5: A5, a6: A6) => IterableIterator ): (a1: A1, a2: A2, a3: A3, a4: A4, a5: A5, a6: A6) => Promise export function flow( + name: string, generator: (a1: A1, a2: A2, a3: A3, a4: A4, a5: A5, a6: A6, a7: A7) => IterableIterator ): (a1: A1, a2: A2, a3: A3, a4: A4, a5: A5, a6: A6, a7: A7) => Promise export function flow( + name: string, generator: ( a1: A1, a2: A2, @@ -39,8 +49,8 @@ export function flow( * @alias flow * @returns {Promise} */ -export function flow(asyncAction: any): any { - return createFlowSpawner(asyncAction.name, asyncAction) +export function flow(name: string, asyncAction: any): any { + return createFlowSpawner(name, asyncAction) } export function createFlowSpawner(name: string, generator: Function) { diff --git a/packages/mobx-state-tree/src/core/process.ts b/packages/mobx-state-tree/src/core/process.ts index 3de5e6018..5e75c818f 100644 --- a/packages/mobx-state-tree/src/core/process.ts +++ b/packages/mobx-state-tree/src/core/process.ts @@ -1,7 +1,7 @@ /* All contents of this file are deprecated. - The term `process` has been replaced with `flow` to avoid conflicts with the + The term `process` has been replaced with `flow` to avoid conflicts with the global `process` object. Refer to `flow.ts` for any further changes to this implementation. @@ -44,7 +44,7 @@ export function process( ) => IterableIterator ): (a1: A1, a2: A2, a3: A3, a4: A4, a5: A5, a6: A6, a7: A7, a8: A8) => Promise /** - * @deprecated has been renamed to `flow()`. + * @deprecated has been renamed to `flow()`. * See https://github.com/mobxjs/mobx-state-tree/issues/399 for more information. * Note that the middleware event types starting with `process` now start with `flow`. * @@ -54,7 +54,7 @@ export function process( */ export function process(asyncAction: any): any { deprecated("process", "`process()` has been renamed to `flow()`. " + DEPRECATION_MESSAGE) - return flow(asyncAction) + return flow(asyncAction.name, asyncAction) } export function createProcessSpawner(name: string, generator: Function) { diff --git a/packages/mobx-state-tree/src/types/complex-types/model.ts b/packages/mobx-state-tree/src/types/complex-types/model.ts index fb7edca0e..2c116e1e5 100644 --- a/packages/mobx-state-tree/src/types/complex-types/model.ts +++ b/packages/mobx-state-tree/src/types/complex-types/model.ts @@ -14,6 +14,7 @@ import { fail, isPlainObject, isPrimitive, + isGenerator, EMPTY_ARRAY, EMPTY_OBJECT, addHiddenFinalProp @@ -39,6 +40,7 @@ import { } from "../type-checker" import { getPrimitiveFactoryFromValue, undefinedType } from "../primitives" import { optional } from "../utility-types/optional" +import { flow } from "../../core/flow" const PRE_PROCESS_SNAPSHOT = "preProcessSnapshot" @@ -165,8 +167,14 @@ export class ModelType extends ComplexType implements IModelType {}.constructor +export function isGenerator(fn: Function): boolean { + return fn instanceof GeneratorType +} From 6db9c1def2bf5726b13dbd2b5ef5235ed63eb07c Mon Sep 17 00:00:00 2001 From: Fabien JUIF Date: Tue, 31 Oct 2017 16:03:53 +0100 Subject: [PATCH 2/2] :sparkles: auto flow - 2 --- packages/mobx-state-tree/src/core/flow.ts | 187 +++++++++--------- packages/mobx-state-tree/src/core/process.ts | 6 +- .../src/types/complex-types/model.ts | 8 +- packages/mobx-state-tree/src/utils.ts | 5 - 4 files changed, 98 insertions(+), 108 deletions(-) diff --git a/packages/mobx-state-tree/src/core/flow.ts b/packages/mobx-state-tree/src/core/flow.ts index e6cd6c0fc..0f166b334 100644 --- a/packages/mobx-state-tree/src/core/flow.ts +++ b/packages/mobx-state-tree/src/core/flow.ts @@ -1,36 +1,26 @@ // based on: https://github.com/mobxjs/mobx-utils/blob/master/src/async-action.ts -export function flow(name: string, generator: () => IterableIterator): () => Promise -export function flow( - name: string, - generator: (a1: A1) => IterableIterator -): (a1: A1) => Promise // Ideally we want to have R instead of Any, but cannot specify R without specifying A1 etc... 'any' as result is better then not specifying request args +export function flow(generator: () => IterableIterator): () => Promise +export function flow(generator: (a1: A1) => IterableIterator): (a1: A1) => Promise // Ideally we want to have R instead of Any, but cannot specify R without specifying A1 etc... 'any' as result is better then not specifying request args export function flow( - name: string, generator: (a1: A1, a2: A2) => IterableIterator ): (a1: A1, a2: A2) => Promise export function flow( - name: string, generator: (a1: A1, a2: A2, a3: A3) => IterableIterator ): (a1: A1, a2: A2, a3: A3) => Promise export function flow( - name: string, generator: (a1: A1, a2: A2, a3: A3, a4: A4) => IterableIterator ): (a1: A1, a2: A2, a3: A3, a4: A4) => Promise export function flow( - name: string, generator: (a1: A1, a2: A2, a3: A3, a4: A4, a5: A5) => IterableIterator ): (a1: A1, a2: A2, a3: A3, a4: A4, a5: A5) => Promise export function flow( - name: string, generator: (a1: A1, a2: A2, a3: A3, a4: A4, a5: A5, a6: A6) => IterableIterator ): (a1: A1, a2: A2, a3: A3, a4: A4, a5: A5, a6: A6) => Promise export function flow( - name: string, generator: (a1: A1, a2: A2, a3: A3, a4: A4, a5: A5, a6: A6, a7: A7) => IterableIterator ): (a1: A1, a2: A2, a3: A3, a4: A4, a5: A5, a6: A6, a7: A7) => Promise export function flow( - name: string, generator: ( a1: A1, a2: A2, @@ -49,103 +39,108 @@ export function flow( * @alias flow * @returns {Promise} */ -export function flow(name: string, asyncAction: any): any { - return createFlowSpawner(name, asyncAction) +export function flow(asyncAction: any): any { + return { + $mst_flow: true, + spawner: createFlowSpawner(asyncAction) + } } -export function createFlowSpawner(name: string, generator: Function) { - const spawner = function flowSpawner(this: any) { - // Implementation based on https://github.com/tj/co/blob/master/index.js - const runId = getNextActionId() - const baseContext = getActionContext() - const args = arguments - - function wrap(fn: any, type: IMiddlewareEventType, arg: any) { - fn.$mst_middleware = (spawner as any).$mst_middleware // pick up any middleware attached to the flow - runWithActionContext( - { - name, - type, - id: runId, - args: [arg], - tree: baseContext.tree, - context: baseContext.context, - parentId: baseContext.id, - rootId: baseContext.rootId - }, - fn - ) - } +export function createFlowSpawner(generator: Function) { + return function namedFlowSpawner(name: string) { + const spawner = function flowSpawner(this: any) { + // Implementation based on https://github.com/tj/co/blob/master/index.js + const runId = getNextActionId() + const baseContext = getActionContext() + const args = arguments - return new Promise(function(resolve, reject) { - let gen: any - const init = function asyncActionInit() { - gen = generator.apply(null, arguments) - onFulfilled(undefined) // kick off the flow + function wrap(fn: any, type: IMiddlewareEventType, arg: any) { + fn.$mst_middleware = (spawner as any).$mst_middleware // pick up any middleware attached to the flow + runWithActionContext( + { + name, + type, + id: runId, + args: [arg], + tree: baseContext.tree, + context: baseContext.context, + parentId: baseContext.id, + rootId: baseContext.rootId + }, + fn + ) } - ;(init as any).$mst_middleware = (spawner as any).$mst_middleware - runWithActionContext( - { - name, - type: "flow_spawn", - id: runId, - args: argsToArray(args), - tree: baseContext.tree, - context: baseContext.context, - parentId: baseContext.id, - rootId: baseContext.rootId - }, - init - ) + return new Promise(function(resolve, reject) { + let gen: any + const init = function asyncActionInit() { + gen = generator.apply(null, arguments) + onFulfilled(undefined) // kick off the flow + } + ;(init as any).$mst_middleware = (spawner as any).$mst_middleware + + runWithActionContext( + { + name, + type: "flow_spawn", + id: runId, + args: argsToArray(args), + tree: baseContext.tree, + context: baseContext.context, + parentId: baseContext.id, + rootId: baseContext.rootId + }, + init + ) - function onFulfilled(res: any) { - let ret - try { - // prettier-ignore - wrap((r: any) => { ret = gen.next(r) }, "flow_resume", res) - } catch (e) { - // prettier-ignore - setImmediate(() => { - wrap((r: any) => { reject(e) }, "flow_throw", e) - }) + function onFulfilled(res: any) { + let ret + try { + // prettier-ignore + wrap((r: any) => { ret = gen.next(r) }, "flow_resume", res) + } catch (e) { + // prettier-ignore + setImmediate(() => { + wrap((r: any) => { reject(e) }, "flow_throw", e) + }) + return + } + next(ret) return } - next(ret) - return - } - function onRejected(err: any) { - let ret - try { - // prettier-ignore - wrap((r: any) => { ret = gen.throw(r) }, "flow_resume_error", err) // or yieldError? - } catch (e) { - // prettier-ignore - setImmediate(() => { - wrap((r: any) => { reject(e) }, "flow_throw", e) - }) - return + function onRejected(err: any) { + let ret + try { + // prettier-ignore + wrap((r: any) => { ret = gen.throw(r) }, "flow_resume_error", err) // or yieldError? + } catch (e) { + // prettier-ignore + setImmediate(() => { + wrap((r: any) => { reject(e) }, "flow_throw", e) + }) + return + } + next(ret) } - next(ret) - } - function next(ret: any) { - if (ret.done) { - // prettier-ignore - setImmediate(() => { - wrap((r: any) => { resolve(r) }, "flow_return", ret.value) - }) - return + function next(ret: any) { + if (ret.done) { + // prettier-ignore + setImmediate(() => { + wrap((r: any) => { resolve(r) }, "flow_return", ret.value) + }) + return + } + // TODO: support more type of values? See https://github.com/tj/co/blob/249bbdc72da24ae44076afd716349d2089b31c4c/index.js#L100 + if (!ret.value || typeof ret.value.then !== "function") + fail("Only promises can be yielded to `async`, got: " + ret) + return ret.value.then(onFulfilled, onRejected) } - // TODO: support more type of values? See https://github.com/tj/co/blob/249bbdc72da24ae44076afd716349d2089b31c4c/index.js#L100 - if (!ret.value || typeof ret.value.then !== "function") - fail("Only promises can be yielded to `async`, got: " + ret) - return ret.value.then(onFulfilled, onRejected) - } - }) + }) + } + return spawner } - return spawner } import { diff --git a/packages/mobx-state-tree/src/core/process.ts b/packages/mobx-state-tree/src/core/process.ts index 5e75c818f..eb19517b0 100644 --- a/packages/mobx-state-tree/src/core/process.ts +++ b/packages/mobx-state-tree/src/core/process.ts @@ -54,15 +54,15 @@ export function process( */ export function process(asyncAction: any): any { deprecated("process", "`process()` has been renamed to `flow()`. " + DEPRECATION_MESSAGE) - return flow(asyncAction.name, asyncAction) + return flow(asyncAction) } -export function createProcessSpawner(name: string, generator: Function) { +export function createProcessSpawner(generator: Function) { deprecated( "process", "`createProcessSpawner()` has been renamed to `createFlowSpawner()`. " + DEPRECATION_MESSAGE ) - return createFlowSpawner(name, generator) + return createFlowSpawner(generator) } import { deprecated } from "../utils" diff --git a/packages/mobx-state-tree/src/types/complex-types/model.ts b/packages/mobx-state-tree/src/types/complex-types/model.ts index 2c116e1e5..eebc3bd77 100644 --- a/packages/mobx-state-tree/src/types/complex-types/model.ts +++ b/packages/mobx-state-tree/src/types/complex-types/model.ts @@ -14,7 +14,6 @@ import { fail, isPlainObject, isPrimitive, - isGenerator, EMPTY_ARRAY, EMPTY_OBJECT, addHiddenFinalProp @@ -40,7 +39,6 @@ import { } from "../type-checker" import { getPrimitiveFactoryFromValue, undefinedType } from "../primitives" import { optional } from "../utility-types/optional" -import { flow } from "../../core/flow" const PRE_PROCESS_SNAPSHOT = "preProcessSnapshot" @@ -170,9 +168,11 @@ export class ModelType extends ComplexType implements IModelType {}.constructor -export function isGenerator(fn: Function): boolean { - return fn instanceof GeneratorType -}