Skip to content

Commit

Permalink
# v4.5.5 2022-05-29
Browse files Browse the repository at this point in the history
* [design] add `Flows.block` WorkFlow mode.
* [design] update `Flows.debounce` interface.
  • Loading branch information
wangyi committed May 29, 2022
1 parent 1a73df5 commit 34c74ac
Show file tree
Hide file tree
Showing 9 changed files with 177 additions and 11 deletions.
11 changes: 9 additions & 2 deletions docs/api.md
Original file line number Diff line number Diff line change
Expand Up @@ -672,19 +672,26 @@ return a decorator callback.
A set class for storing common `WorkFlows`.
```typescript
type BlockFlowConfig = {timeout?:number};

type DebounceFlowConfig = {time:number, leading?:boolean};

export class Flows {

static default():WorkFlow;

static latest():WorkFlow;

static debounce(ms:number, leading?:boolean):WorkFlow;
static debounce(time:number|DebounceFlowConfig, leading?:boolean):WorkFlow;

static block(timeout?:number|BlockFlowConfig):WorkFlow;
}
```
* Flows.default - use default work flow, which just like `@flow()`.
* Flows.latest - to take state changes which is leaded by the newest calling of a flow method.
* Flows.debounce - to make the flow method work with a debounce effect.
* Flows.debounce - to make the flow method work with a debounce effect.
* Flows.block - to make flow method work atomically. It is always used on an async method, to prevent this method works again when it has not finished. The `timeout` set can release the block state, if the async method is not finished for a too long time.
## effect
Expand Down
7 changes: 6 additions & 1 deletion docs/changes.md
Original file line number Diff line number Diff line change
Expand Up @@ -203,4 +203,9 @@ in this version, `runtime.cache` used in MiddleWare is independent.

# 4.5.4 2022-05-25

* [rollback] rollback to `4.4.0`.
* [rollback] rollback to `4.4.0`.

# v4.5.5 2022-05-29

* [design] add `Flows.block` WorkFlow mode.
* [design] update `Flows.debounce` interface.
11 changes: 9 additions & 2 deletions docs/zh/api.md
Original file line number Diff line number Diff line change
Expand Up @@ -654,22 +654,29 @@ export declare const flow:FlowFn;
## Flows
工作模式集合,目前有:`Flows.latest()``Flows.debounce()` 两个成员
工作模式集合。
```typescript
type BlockFlowConfig = {timeout?:number};

type DebounceFlowConfig = {time:number, leading?:boolean};

export class Flows {

static default():WorkFlow;

static latest():WorkFlow;

static debounce(ms:number, leading?:boolean):WorkFlow;
static debounce(ms:number|DebounceFlowConfig, leading?:boolean):WorkFlow;

static block(timeout?:number|BlockFlowConfig):WorkFlow;
}
```
* Flows.default - 默认工作模式,和无传参的 `@flow()` 等效。
* Flows.latest - 只允许最新工作流方法产生的 state 变更生效.
* Flows.debounce - 使工作流方法以 debounce 防抖模式运行.
* Flows.block - 工作流方法以原子性特性运行。如一个方法没有结束,则不能再次运行。多用于Promise返回类型的异步方法。
## effect
Expand Down
7 changes: 6 additions & 1 deletion docs/zh/changes.md
Original file line number Diff line number Diff line change
Expand Up @@ -211,4 +211,9 @@

# 4.5.4 2022-05-25

* [rollback] 回滚至 `4.4.0`.
* [rollback] 回滚至 `4.4.0`.

# v4.5.5 2022-05-29

* [design] 添加 `Flows.block` 工作模式。
* [design] 更新 `Flows.debounce` 接口。
8 changes: 7 additions & 1 deletion index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -212,6 +212,10 @@ export type FlowRuntime = {

export type WorkFlow = (runtime:FlowRuntime)=>LaunchHandler;

type BlockFlowConfig = {timeout?:number};

type DebounceFlowConfig = {time:number, leading?:boolean};

declare type FlowFn =((...flows:WorkFlow[])=>MethodDecoratorCaller)&{
force:<S=any, T extends Model<S>=Model<S>>(target:T, workFlow?:WorkFlow)=>T,
error:<
Expand All @@ -227,7 +231,9 @@ export class Flows {

static latest():WorkFlow;

static debounce(ms:number, leading?:boolean):WorkFlow;
static debounce(ms:number|DebounceFlowConfig, leading?:boolean):WorkFlow;

static block(timeout?:number|BlockFlowConfig):WorkFlow;
}

export class MiddleWares {
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "agent-reducer",
"version": "4.5.4",
"version": "4.5.5",
"main": "dist/agent-reducer.mini.js",
"typings": "index.d.ts",
"author": "Jimmy.Harding",
Expand Down
51 changes: 48 additions & 3 deletions src/libs/flows.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import { WorkFlow, FlowRuntime, LaunchHandler } from './global.type';
import { isPromise } from './util';
import {
WorkFlow, FlowRuntime, LaunchHandler, BlockFlowConfig, DebounceFlowConfig,
} from './global.type';
import { isPromise, noop } from './util';

function promise():WorkFlow {
return function process(runtime:FlowRuntime):LaunchHandler {
Expand Down Expand Up @@ -41,7 +43,9 @@ export class Flows {
};
}

static debounce(ms:number, leading?:boolean):WorkFlow {
static debounce(time:number|DebounceFlowConfig, lead?:boolean):WorkFlow {
const ms = typeof time === 'number' ? time : time.time;
const leading = typeof time === 'number' ? lead : time.leading;
if (leading) {
return function leadingProcess(runtime:FlowRuntime):LaunchHandler {
const { state } = runtime;
Expand Down Expand Up @@ -71,6 +75,47 @@ export class Flows {
};
};
}

static block(config?:number|BlockFlowConfig):WorkFlow {
const timeout = (function computeTimeout() {
if (config == null) {
return undefined;
}
if (typeof config === 'number') {
return config;
}
return config.timeout;
}());
function processPromiseFinished(rt:Promise<unknown>, time?:number):Promise<unknown> {
const mainPromise = Promise.resolve(rt).then(noop, noop);
const timeoutPromise = time == null
? mainPromise : new Promise<void>((r) => setTimeout(r, time));
return Promise.race([mainPromise, timeoutPromise]);
}
return function process(runtime:FlowRuntime):LaunchHandler {
const { state } = runtime;
return {
invoke(method) {
return function blockMethod(...args:any[]) {
if (state.running) {
return state.result;
}
state.running = true;
const result = method(...args);
state.result = result;
if (!isPromise(result)) {
state.running = false;
return state.result;
}
processPromiseFinished(result, timeout).then(() => {
state.running = false;
});
return state.result;
};
},
};
};
}
}

export const defaultFlow = promise();
4 changes: 4 additions & 0 deletions src/libs/global.type.ts
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,10 @@ export type LaunchHandler = {

export type WorkFlow = (runtime:FlowRuntime)=>LaunchHandler;

export type DebounceFlowConfig = {time:number, leading?:boolean};

export type BlockFlowConfig = {timeout?:number};

export type Runtime<T extends Record<string, any>=any> = {
methodName: string;
args?: any[];
Expand Down
87 changes: 87 additions & 0 deletions test/experience/act.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -276,3 +276,90 @@ describe('use `flow.force` API',()=>{
});

});

describe('use Flows',()=>{

type User = {
id?: number,
username: string,
role?: 'master' | 'user' | 'guest',
password?: string
name?: string,
age?: number,
sex?: 'male' | 'female'
};

class UserModel implements Model<User> {

state: User = {
username: 'guest'
};

updateUser(user: User): User {
return user;
}

@flow(Flows.block())
async fetchUser(username: string) {
const user: User = await new Promise((resolve, reject) => {
setTimeout(() => {
resolve({
id: 1,
username: username,
name: username,
role: 'user',
age: 20
} as User);
},200);
});
this.updateUser(user);
}

@flow(Flows.block({timeout:100}))
async fetchUserInTime(username: string) {
const user: User = await new Promise((resolve, reject) => {
setTimeout(() => {
resolve({
id: 1,
username: username,
name: username,
role: 'user',
age: 20
} as User);
},200);
});
this.updateUser(user);
}

}

test('use `Flows.block` to block a method until this method is truly end',async ()=>{
const {agent, connect, disconnect} = create(UserModel);
connect();
const p1 = agent.fetchUser('ab');
const p2 = agent.fetchUser('bc')
await Promise.all([p1,p2]);

expect(agent.state.username).toBe('ab');
disconnect();
});

test('use `Flows.block` to block a method until this method is truly end, or over 100 ms',async ()=>{
const {agent, connect, disconnect} = create(UserModel);
connect();
const p1 = agent.fetchUserInTime('ab');
const p2 = agent.fetchUserInTime('bc');
const p3 = Promise.all([p1,p2]);
const p4 = new Promise((resolve)=>{
setTimeout(()=>{
resolve(agent.fetchUserInTime('cd'));
},100);
});
await p3;
expect(agent.state.username).toBe('ab');
await Promise.all([p3,p4]);
expect(agent.state.username).toBe('cd');
disconnect();
});

});

0 comments on commit 34c74ac

Please sign in to comment.