Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

✨ auto flow - 2 #2

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
171 changes: 88 additions & 83 deletions packages/mobx-state-tree/src/core/flow.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,102 +40,107 @@ export function flow<A1, A2, A3, A4, A5, A6, A7, A8>(
* @returns {Promise}
*/
export function flow(asyncAction: any): any {
return createFlowSpawner(asyncAction.name, asyncAction)
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 {
Expand Down
8 changes: 4 additions & 4 deletions packages/mobx-state-tree/src/core/process.ts
Original file line number Diff line number Diff line change
@@ -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.
Expand Down Expand Up @@ -44,7 +44,7 @@ export function process<A1, A2, A3, A4, A5, A6, A7, A8>(
) => IterableIterator<any>
): (a1: A1, a2: A2, a3: A3, a4: A4, a5: A5, a6: A6, a7: A7, a8: A8) => Promise<any>
/**
* @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`.
*
Expand All @@ -57,12 +57,12 @@ export function process(asyncAction: any): any {
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"
Expand Down
10 changes: 9 additions & 1 deletion packages/mobx-state-tree/src/types/complex-types/model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -165,8 +165,16 @@ export class ModelType<S, T> extends ComplexType<S, T> implements IModelType<S,
`Cannot define action '${PRE_PROCESS_SNAPSHOT}', it should be defined using 'type.preProcessSnapshot(fn)' instead`
)

// apply hook composition
let action = actions[name]

// apply flow
// FIXME: fix cast
if ((action as any).$mst_flow) {
action = (action as any).spawner(name)
}
/* tslint:enable */

// apply hook composition
let baseAction = (self as any)[name]
if (name in HOOK_NAMES && baseAction) {
let specializedAction = action
Expand Down