diff --git a/.gitignore b/.gitignore index 3503468..d94f532 100644 --- a/.gitignore +++ b/.gitignore @@ -30,4 +30,4 @@ yarn-error.log .DS_Store Thumbs.db -playground/gxc-canvas-viewer/**/*.* \ No newline at end of file +playground/*/**/*.* \ No newline at end of file diff --git a/lib/src/executers/abstract-executor.ts b/lib/src/executers/abstract-executor.ts index fdecf6b..217e983 100644 --- a/lib/src/executers/abstract-executor.ts +++ b/lib/src/executers/abstract-executor.ts @@ -1,15 +1,9 @@ import { isIgnorableKey, isImmutableKey } from "@gxc-solutions/model-base/decorators"; import { IModel } from "@gxc-solutions/model-base/interfaces"; -import { Subject } from "rxjs"; -import { ICommand, ICommandExecuter, ICommandResult } from "../interfaces"; +import { ICommandAndArg, ICommandExecuter, ICommandResult } from "../interfaces"; export abstract class AbstractExecutor implements ICommandExecuter { - protected _changes$ = new Subject[]>(); - readonly changes$ = this._changes$.asObservable(); - - abstract execute(command: ICommand, object: T): this; - - abstract flush(): Promise; + abstract execute(context: ICommandAndArg[]): Promise[]>; protected _apply(object: T, values: Partial): boolean { return Object.keys(values).some((key) => { diff --git a/lib/src/executers/collector.ts b/lib/src/executers/collector.ts new file mode 100644 index 0000000..7a9cbc4 --- /dev/null +++ b/lib/src/executers/collector.ts @@ -0,0 +1,17 @@ +import { IModel } from "@gxc-solutions/model-base"; +import { ICommand, ICommandAndArg, ICommandArguments } from "../interfaces"; + +export class CommandsCollector { + private _commands: ICommandAndArg[] = []; + + constructor(private _: (commands: ICommandAndArg[]) => Promise) {} + + push(command: ICommand, args: ICommandArguments) { + this._commands.push({ command, args }); + return this; + } + + flush() { + this._(this._commands); + } +} diff --git a/lib/src/executers/executor.ts b/lib/src/executers/executor.ts deleted file mode 100644 index 6e5a704..0000000 --- a/lib/src/executers/executor.ts +++ /dev/null @@ -1,27 +0,0 @@ -import { IModel } from "@gxc-solutions/model-base/interfaces"; -import { IIndexer } from "../interfaces"; -import { ParallelCommandExecutor } from "./parallel-executor"; -import { Queue } from "./queue"; -import { SerialExecutor } from "./serial-executor"; - -export class CommandsExecutor { - private _queue = new Queue(); - private _parallel: ParallelCommandExecutor; - private _serial: SerialExecutor; - - constructor(private _indexer: IIndexer) { - this._parallel = new ParallelCommandExecutor(this._indexer); - this._serial = new SerialExecutor(this._indexer); - } - - async executeParallel() { - while (!this._queue.isEmpty()) { - // const flush = this._queue.dequeue(); - // const result = await flush; - } - const result = this._parallel.flush(); - this._queue.enqueue(result); - } - - executeIndependent() {} -} diff --git a/lib/src/executers/index.ts b/lib/src/executers/index.ts new file mode 100644 index 0000000..04071cb --- /dev/null +++ b/lib/src/executers/index.ts @@ -0,0 +1,5 @@ +export * from "./serial-executor"; +export * from "./abstract-executor"; +export * from "./parallel-executor"; +export * from "./collector"; +export * from "./manager"; diff --git a/lib/src/executers/manager.ts b/lib/src/executers/manager.ts new file mode 100644 index 0000000..82c6d19 --- /dev/null +++ b/lib/src/executers/manager.ts @@ -0,0 +1,31 @@ +import { IModel } from "@gxc-solutions/model-base"; +import { Subject } from "rxjs"; +import { ICommandResult, IIndexer } from "../interfaces"; +import { CommandsCollector } from "./collector"; +import { ParallelCommandExecutor } from "./parallel-executor"; +import { SerialExecutor } from "./serial-executor"; + +export type RunType = "parallel" | "serial"; + +export class ExecuteManager { + protected _changes$ = new Subject[]>(); + readonly changes$ = this._changes$.asObservable(); + + constructor(private _indexer: IIndexer) {} + + run(type: RunType) { + if (type === "parallel") { + return new CommandsCollector(async (commands) => { + const results = await new ParallelCommandExecutor(this._indexer).execute(commands); + this._changes$.next(results); + }); + } else if (type === "serial") { + return new CommandsCollector(async (commands) => { + const results = await new SerialExecutor(this._indexer).execute(commands); + this._changes$.next(results); + }); + } else { + throw new Error("Unknown run type!"); + } + } +} diff --git a/lib/src/executers/parallel-executor.ts b/lib/src/executers/parallel-executor.ts index 9995d0a..5fa768e 100644 --- a/lib/src/executers/parallel-executor.ts +++ b/lib/src/executers/parallel-executor.ts @@ -1,31 +1,19 @@ import { IModel } from "@gxc-solutions/model-base/interfaces"; -import { ICommand, ICommandResult, IIndexer } from "../interfaces"; +import { ICommandAndArg, ICommandResult, IIndexer } from "../interfaces"; import { AbstractExecutor } from "./abstract-executor"; -import { Queue } from "./queue"; // Set of actions for apply once export class ParallelCommandExecutor extends AbstractExecutor { - private _results: Promise[]>[] = []; - private _queue: Queue; - constructor(private _indexer: IIndexer) { super(); } - execute(command: ICommand, object: T): this { - const result = command.execute(object); - this._results.push(result); - - return this; - } - - async flush() { - if (this._results.length > 0) { - const allResults = await Promise.all(this._results); + async execute(context: ICommandAndArg[]): Promise[]> { + if (context.length > 0) { + const allResults = await Promise.all(context.map(({ command, args }) => command.execute(args))); const isWrongResult = this._checkResults(allResults); if (isWrongResult) { - this._results = []; throw new Error("Commands changed the same object multiple times. This is not allowed."); } @@ -36,12 +24,11 @@ export class ParallelCommandExecutor extends AbstractExecutor< }); if (!isChanged) { - this._results = []; - return; + return []; } - this._changes$.next(flattenResults); + return flattenResults; } - this._results = []; + return []; } private _checkResults(results: ICommandResult[][]): boolean { diff --git a/lib/src/executers/serial-executor-2.ts b/lib/src/executers/serial-executor-2.ts deleted file mode 100644 index ce1f3dc..0000000 --- a/lib/src/executers/serial-executor-2.ts +++ /dev/null @@ -1,43 +0,0 @@ -import { IModel } from "@gxc-solutions/model-base/interfaces"; -import { ICommand, IIndexer } from "../interfaces"; -import { AbstractExecutor } from "./abstract-executor"; - -interface ICommandAndArg { - command: ICommand; - object: T; -} - -// Set of actions for serial execution -export class SerialExecutor extends AbstractExecutor { - private _commands: ICommandAndArg[] = []; - private _isFlushed = false; - - constructor(private _indexer: IIndexer) { - super(); - } - - execute(command: ICommand, object: T): this { - if (this._isFlushed) { - throw new Error("Cannot execute commands after flush has been called."); - } - this._commands.push({ command, object }); - return this; - } - - async flush(): Promise { - this._isFlushed = true; - for (const { command, object } of this._commands) { - const commandResult = await command.execute(object); - const isChanged = commandResult.map(({ id, result /*type */ }) => { - const object = this._indexer.get(id); - return this._apply(object, result); - }); - if (isChanged) { - this._changes$.next(commandResult); - } - } - - this._commands = []; - this._isFlushed = false; - } -} diff --git a/lib/src/executers/serial-executor.ts b/lib/src/executers/serial-executor.ts index 09e71bc..dca18ff 100644 --- a/lib/src/executers/serial-executor.ts +++ b/lib/src/executers/serial-executor.ts @@ -1,29 +1,17 @@ import { IModel } from "@gxc-solutions/model-base/interfaces"; -import { ICommand, ICommandResult, IIndexer } from "../interfaces"; +import { ICommandAndArg, ICommandResult, IIndexer } from "../interfaces"; import { AbstractExecutor } from "./abstract-executor"; -interface ICommandAndArg { - command: ICommand; - object: T; -} - // Set of actions for serial execution export class SerialExecutor extends AbstractExecutor { - private _commands: ICommandAndArg[] = []; - constructor(private _indexer: IIndexer) { super(); } - execute(command: ICommand, object: T): this { - this._commands.push({ command, object }); - return this; - } - - async flush(): Promise { + async execute(context: ICommandAndArg[]): Promise[]> { const results = new Map>(); - for (const { command, object } of this._commands) { + for (const { command, args: object } of context) { const commandResult = await command.execute(object); commandResult.map(({ id, result, type }) => { if (results.has(id)) { @@ -40,9 +28,8 @@ export class SerialExecutor extends AbstractExecutor { return this._apply(object, result.result); }); if (!isChanged) { - this._commands = []; - return; + return []; } - this._changes$.next(Array.from(results.values())); + return Array.from(results.values()); } } diff --git a/lib/src/interfaces/executer.ts b/lib/src/interfaces/executer.ts index 5c4d42d..2bef506 100644 --- a/lib/src/interfaces/executer.ts +++ b/lib/src/interfaces/executer.ts @@ -1,14 +1,20 @@ import { IModel } from "@gxc-solutions/model-base/interfaces"; -import { Observable } from "rxjs"; + +export interface ICommandAndArg { + command: ICommand; + args: ICommandArguments; +} + +export interface ICommandArguments { + model: T; +} export interface ICommandExecuter { - changes$: Observable[]>; - execute(command: ICommand, object: T): this; - flush(): Promise; + execute(context: ICommandAndArg[]): Promise[]>; } export interface ICommand { - execute(object: T): Promise[]>; + execute(args: ICommandArguments): Promise[]>; } export interface ICommandResult { diff --git a/lib/src/package.json b/lib/src/package.json index 019cfdc..8281c44 100644 --- a/lib/src/package.json +++ b/lib/src/package.json @@ -1,6 +1,6 @@ { "name": "@gxc-solutions/command-executer", - "version": "0.0.1", + "version": "0.0.2", "main": "index.js", "author": "GXC Solutions", "publishConfig": { diff --git a/package.json b/package.json index 700abbd..3677c9b 100644 --- a/package.json +++ b/package.json @@ -1,5 +1,5 @@ { - "name": "template-of-lib-repo", + "name": "gxc-command-executer", "version": "0.0.0", "main": "index.js", "scripts": { diff --git a/tsconfig.dev.json b/tsconfig.dev.json index 6746dc4..2029901 100644 --- a/tsconfig.dev.json +++ b/tsconfig.dev.json @@ -1,7 +1,7 @@ { "compilerOptions": { "rootDir": "./lib/src", - "outDir": "./playground/lib", + "outDir": "./playground/command-executer", }, "extends": ["./tsconfig.json"], "exclude": ["./node_modules/*", "./lib/**/*.test.ts"]