зеркало из https://github.com/microsoft/p-graph.git
adding pgraph
This commit is contained in:
Родитель
8127e8bb1d
Коммит
c68fdd3519
|
@ -1,104 +1,3 @@
|
|||
# Logs
|
||||
logs
|
||||
*.log
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
lerna-debug.log*
|
||||
|
||||
# Diagnostic reports (https://nodejs.org/api/report.html)
|
||||
report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
|
||||
|
||||
# Runtime data
|
||||
pids
|
||||
*.pid
|
||||
*.seed
|
||||
*.pid.lock
|
||||
|
||||
# Directory for instrumented libs generated by jscoverage/JSCover
|
||||
lib-cov
|
||||
|
||||
# Coverage directory used by tools like istanbul
|
||||
coverage
|
||||
*.lcov
|
||||
|
||||
# nyc test coverage
|
||||
.nyc_output
|
||||
|
||||
# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
|
||||
.grunt
|
||||
|
||||
# Bower dependency directory (https://bower.io/)
|
||||
bower_components
|
||||
|
||||
# node-waf configuration
|
||||
.lock-wscript
|
||||
|
||||
# Compiled binary addons (https://nodejs.org/api/addons.html)
|
||||
build/Release
|
||||
|
||||
# Dependency directories
|
||||
node_modules/
|
||||
jspm_packages/
|
||||
|
||||
# TypeScript v1 declaration files
|
||||
typings/
|
||||
|
||||
# TypeScript cache
|
||||
*.tsbuildinfo
|
||||
|
||||
# Optional npm cache directory
|
||||
.npm
|
||||
|
||||
# Optional eslint cache
|
||||
.eslintcache
|
||||
|
||||
# Microbundle cache
|
||||
.rpt2_cache/
|
||||
.rts2_cache_cjs/
|
||||
.rts2_cache_es/
|
||||
.rts2_cache_umd/
|
||||
|
||||
# Optional REPL history
|
||||
.node_repl_history
|
||||
|
||||
# Output of 'npm pack'
|
||||
*.tgz
|
||||
|
||||
# Yarn Integrity file
|
||||
.yarn-integrity
|
||||
|
||||
# dotenv environment variables file
|
||||
.env
|
||||
.env.test
|
||||
|
||||
# parcel-bundler cache (https://parceljs.org/)
|
||||
.cache
|
||||
|
||||
# Next.js build output
|
||||
.next
|
||||
|
||||
# Nuxt.js build / generate output
|
||||
.nuxt
|
||||
dist
|
||||
|
||||
# Gatsby files
|
||||
.cache/
|
||||
# Comment in the public line in if your project uses Gatsby and *not* Next.js
|
||||
# https://nextjs.org/blog/next-9-1#public-directory-support
|
||||
# public
|
||||
|
||||
# vuepress build output
|
||||
.vuepress/dist
|
||||
|
||||
# Serverless directories
|
||||
.serverless/
|
||||
|
||||
# FuseBox cache
|
||||
.fusebox/
|
||||
|
||||
# DynamoDB Local files
|
||||
.dynamodb/
|
||||
|
||||
# TernJS port file
|
||||
.tern-port
|
||||
node_modules
|
||||
lib
|
||||
*.log
|
|
@ -0,0 +1,3 @@
|
|||
src
|
||||
tsconfig.json
|
||||
yarn.lock
|
|
@ -0,0 +1,192 @@
|
|||
{
|
||||
"name": "p-graph",
|
||||
"entries": [
|
||||
{
|
||||
"date": "Tue, 05 May 2020 21:42:11 GMT",
|
||||
"tag": "p-graph_v0.4.0",
|
||||
"version": "0.4.0",
|
||||
"comments": {
|
||||
"none": [
|
||||
{
|
||||
"comment": "adding test before release",
|
||||
"author": "kchau@microsoft.com",
|
||||
"commit": "4acba927925474f330d00abcbdfb85b261185bec",
|
||||
"package": "p-graph"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"date": "Tue, 05 May 2020 21:41:06 GMT",
|
||||
"tag": "p-graph_v0.4.0",
|
||||
"version": "0.4.0",
|
||||
"comments": {
|
||||
"patch": [
|
||||
{
|
||||
"comment": "fix typings",
|
||||
"author": "kchau@microsoft.com",
|
||||
"commit": "8c344d7555cece152271094fb19e0a2d454aab61",
|
||||
"package": "p-graph"
|
||||
}
|
||||
],
|
||||
"minor": [
|
||||
{
|
||||
"comment": "ripped out the dependency on p-queue",
|
||||
"author": "kchau@microsoft.com",
|
||||
"commit": "0471bdb2615e368c3d801798a255d603569ddb64",
|
||||
"package": "p-graph"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"date": "Fri, 01 May 2020 20:41:56 GMT",
|
||||
"tag": "p-graph_v0.3.4",
|
||||
"version": "0.3.4",
|
||||
"comments": {
|
||||
"patch": [
|
||||
{
|
||||
"comment": "process the args accurately",
|
||||
"author": "kchau@microsoft.com",
|
||||
"commit": "7fe3fabf056ce9f6c7d4ed5d756e86789602a6dd",
|
||||
"package": "p-graph"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"date": "Fri, 01 May 2020 19:12:59 GMT",
|
||||
"tag": "p-graph_v0.3.3",
|
||||
"version": "0.3.3",
|
||||
"comments": {
|
||||
"patch": [
|
||||
{
|
||||
"comment": "this lib is using esm default to publish, so it's a bit different in how to require",
|
||||
"author": "kchau@microsoft.com",
|
||||
"commit": "9a97d9f11a3da35ec6bc7f6230e51846d6bf73a8",
|
||||
"package": "p-graph"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"date": "Fri, 01 May 2020 18:26:56 GMT",
|
||||
"tag": "p-graph_v0.3.2",
|
||||
"version": "0.3.2",
|
||||
"comments": {
|
||||
"patch": [
|
||||
{
|
||||
"comment": "one more way of doing pGraph() call in types",
|
||||
"author": "kchau@microsoft.com",
|
||||
"commit": "7700bc7f9dea02d7be0dfa1020e5507eaf9d67d4",
|
||||
"package": "p-graph"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"date": "Fri, 01 May 2020 18:25:27 GMT",
|
||||
"tag": "p-graph_v0.3.1",
|
||||
"version": "0.3.1",
|
||||
"comments": {
|
||||
"patch": [
|
||||
{
|
||||
"comment": "one more way of doing pGraph() call in types",
|
||||
"author": "kchau@microsoft.com",
|
||||
"commit": "c1da50e3d23c4807ce8f486015ed33cb65f9f69d",
|
||||
"package": "p-graph"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"date": "Fri, 01 May 2020 18:21:34 GMT",
|
||||
"tag": "p-graph_v0.3.0",
|
||||
"version": "0.3.0",
|
||||
"comments": {
|
||||
"minor": [
|
||||
{
|
||||
"comment": "making public api work with 2 args with options",
|
||||
"author": "kchau@microsoft.com",
|
||||
"commit": "4198ea4a7d95ba40d423d4d98fba9e73f0f750e5",
|
||||
"package": "p-graph"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"date": "Fri, 01 May 2020 17:55:50 GMT",
|
||||
"tag": "p-graph_v0.2.4",
|
||||
"version": "0.2.4",
|
||||
"comments": {
|
||||
"patch": [
|
||||
{
|
||||
"comment": "removed the section of readme that is known not to work",
|
||||
"author": "kchau@microsoft.com",
|
||||
"commit": "e25641e2e34d952eb7ae30af79c3269d340ace23",
|
||||
"package": "p-graph"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"date": "Fri, 01 May 2020 17:39:19 GMT",
|
||||
"tag": "p-graph_v0.2.3",
|
||||
"version": "0.2.3",
|
||||
"comments": {
|
||||
"patch": [
|
||||
{
|
||||
"comment": "fixes the url",
|
||||
"author": "kchau@microsoft.com",
|
||||
"commit": "7c7ebfcd0ef6794ff2f6bffc9bfbd3888fe07447",
|
||||
"package": "p-graph"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"date": "Fri, 01 May 2020 17:37:29 GMT",
|
||||
"tag": "p-graph_v0.2.2",
|
||||
"version": "0.2.2",
|
||||
"comments": {
|
||||
"patch": [
|
||||
{
|
||||
"comment": "updates the readme.md and added a url to the package.json",
|
||||
"author": "kchau@microsoft.com",
|
||||
"commit": "b23b85b41f9adc3ebf8f6e1f936d25ca25640e89",
|
||||
"package": "p-graph"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"date": "Fri, 01 May 2020 16:30:46 GMT",
|
||||
"tag": "p-graph_v0.2.1",
|
||||
"version": "0.2.1",
|
||||
"comments": {
|
||||
"patch": [
|
||||
{
|
||||
"comment": "added types",
|
||||
"author": "kchau@microsoft.com",
|
||||
"commit": "560fba181433eafc55043be0c101613c3dff54fc",
|
||||
"package": "p-graph"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"date": "Fri, 01 May 2020 16:15:38 GMT",
|
||||
"tag": "p-graph_v0.2.0",
|
||||
"version": "0.2.0",
|
||||
"comments": {
|
||||
"minor": [
|
||||
{
|
||||
"author": "kchau@microsoft.com",
|
||||
"commit": "9ee9ae8897acc7213768680b37c1cb3b18234bb6",
|
||||
"package": "p-graph"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
|
@ -0,0 +1,97 @@
|
|||
# Change Log - p-graph
|
||||
|
||||
This log was last generated on Tue, 05 May 2020 21:41:06 GMT and should not be manually modified.
|
||||
|
||||
<!-- Start content -->
|
||||
|
||||
## 0.4.0
|
||||
|
||||
Tue, 05 May 2020 21:41:06 GMT
|
||||
|
||||
### Minor changes
|
||||
|
||||
- ripped out the dependency on p-queue (kchau@microsoft.com)
|
||||
|
||||
### Patches
|
||||
|
||||
- fix typings (kchau@microsoft.com)
|
||||
|
||||
## 0.3.4
|
||||
|
||||
Fri, 01 May 2020 20:41:56 GMT
|
||||
|
||||
### Patches
|
||||
|
||||
- process the args accurately (kchau@microsoft.com)
|
||||
|
||||
## 0.3.3
|
||||
|
||||
Fri, 01 May 2020 19:12:59 GMT
|
||||
|
||||
### Patches
|
||||
|
||||
- this lib is using esm default to publish, so it's a bit different in how to require (kchau@microsoft.com)
|
||||
|
||||
## 0.3.2
|
||||
|
||||
Fri, 01 May 2020 18:26:56 GMT
|
||||
|
||||
### Patches
|
||||
|
||||
- one more way of doing pGraph() call in types (kchau@microsoft.com)
|
||||
|
||||
## 0.3.1
|
||||
|
||||
Fri, 01 May 2020 18:25:27 GMT
|
||||
|
||||
### Patches
|
||||
|
||||
- one more way of doing pGraph() call in types (kchau@microsoft.com)
|
||||
|
||||
## 0.3.0
|
||||
|
||||
Fri, 01 May 2020 18:21:34 GMT
|
||||
|
||||
### Minor changes
|
||||
|
||||
- making public api work with 2 args with options (kchau@microsoft.com)
|
||||
|
||||
## 0.2.4
|
||||
|
||||
Fri, 01 May 2020 17:55:50 GMT
|
||||
|
||||
### Patches
|
||||
|
||||
- removed the section of readme that is known not to work (kchau@microsoft.com)
|
||||
|
||||
## 0.2.3
|
||||
|
||||
Fri, 01 May 2020 17:39:19 GMT
|
||||
|
||||
### Patches
|
||||
|
||||
- fixes the url (kchau@microsoft.com)
|
||||
|
||||
## 0.2.2
|
||||
|
||||
Fri, 01 May 2020 17:37:29 GMT
|
||||
|
||||
### Patches
|
||||
|
||||
- updates the readme.md and added a url to the package.json (kchau@microsoft.com)
|
||||
|
||||
## 0.2.1
|
||||
|
||||
Fri, 01 May 2020 16:30:46 GMT
|
||||
|
||||
### Patches
|
||||
|
||||
- added types (kchau@microsoft.com)
|
||||
|
||||
## 0.2.0
|
||||
|
||||
Fri, 01 May 2020 16:15:38 GMT
|
||||
|
||||
### Minor changes
|
||||
|
||||
- undefined (kchau@microsoft.com)
|
133
README.md
133
README.md
|
@ -1,7 +1,138 @@
|
|||
# p-graph
|
||||
|
||||
Run a promise graph with concurrency control.
|
||||
|
||||
## Install
|
||||
|
||||
```
|
||||
$ npm install p-graph
|
||||
```
|
||||
|
||||
## Usage
|
||||
|
||||
The p-graph library takes in a `graph` argument. To start, create a graph of functions that return promises (let's call them Run Functions), then run them through the pGraph API:
|
||||
|
||||
```js
|
||||
const { default: pGraph } = require("p-graph"); // ES6 import also works: import pGraph from 'p-graph';
|
||||
|
||||
const putOnShirt = () => Promise.resolve("put on your shirt");
|
||||
const putOnShorts = () => Promise.resolve("put on your shorts");
|
||||
const putOnJacket = () => Promise.resolve("put on your jacket");
|
||||
const putOnShoes = () => Promise.resolve("put on your shoes");
|
||||
const tieShoes = () => Promise.resolve("tie your shoes");
|
||||
|
||||
const graph = [
|
||||
[putOnShoes, tieShoes],
|
||||
[putOnShirt, putOnJacket],
|
||||
[putOnShorts, putOnJacket],
|
||||
[putOnShorts, putOnShoes],
|
||||
];
|
||||
|
||||
await pGraph(graph, { concurrency: 3 }).run(); // returns a promise that will resolve when all the tasks are done from this graph in order
|
||||
```
|
||||
|
||||
### Ways to define a graph
|
||||
|
||||
1. Use a dependency array
|
||||
|
||||
```js
|
||||
const putOnShirt = () => Promise.resolve("put on your shirt");
|
||||
const putOnShorts = () => Promise.resolve("put on your shorts");
|
||||
const putOnJacket = () => Promise.resolve("put on your jacket");
|
||||
const putOnShoes = () => Promise.resolve("put on your shoes");
|
||||
const tieShoes = () => Promise.resolve("tie your shoes");
|
||||
|
||||
const graph = [
|
||||
[putOnShoes, tieShoes],
|
||||
[putOnShirt, putOnJacket],
|
||||
[putOnShorts, putOnJacket],
|
||||
[putOnShorts, putOnShoes],
|
||||
];
|
||||
|
||||
await pGraph(graph);
|
||||
```
|
||||
|
||||
2. Use a dependency array with a list of named functions
|
||||
|
||||
```js
|
||||
const funcs = new Map();
|
||||
|
||||
funcs.set("putOnShirt", () => Promise.resolve("put on your shirt"));
|
||||
funcs.set("putOnShorts", () => Promise.resolve("put on your shorts"));
|
||||
funcs.set("putOnJacket", () => Promise.resolve("put on your jacket"));
|
||||
funcs.set("putOnShoes", () => Promise.resolve("put on your shoes"));
|
||||
funcs.set("tieShoes", () => Promise.resolve("tie your shoes"));
|
||||
|
||||
const graph = [
|
||||
[putOnShoes, tieShoes],
|
||||
[putOnShirt, putOnJacket],
|
||||
[putOnShorts, putOnJacket],
|
||||
[putOnShorts, putOnShoes],
|
||||
];
|
||||
|
||||
await pGraph(namedFunctions, graph);
|
||||
```
|
||||
|
||||
3. Use a dependency map with a list of named functions
|
||||
|
||||
```js
|
||||
const funcs = new Map();
|
||||
|
||||
funcs.set("putOnShirt", () => Promise.resolve("put on your shirt"));
|
||||
funcs.set("putOnShorts", () => Promise.resolve("put on your shorts"));
|
||||
funcs.set("putOnJacket", () => Promise.resolve("put on your jacket"));
|
||||
funcs.set("putOnShoes", () => Promise.resolve("put on your shoes"));
|
||||
funcs.set("tieShoes", () => Promise.resolve("tie your shoes"));
|
||||
|
||||
const depMap = new Map();
|
||||
|
||||
depMap.set(tieShoes, new Set([putOnShoes]));
|
||||
depMap.set(putOnJacket, new Set([putOnShirt, putOnShorts]));
|
||||
depMap.set(putOnShoes, new Set([putOnShorts]));
|
||||
depMap.set(putOnShorts, new Set());
|
||||
depMap.set(putOnShirt, new Set());
|
||||
|
||||
await pGraph(namedFunctions, graph);
|
||||
```
|
||||
|
||||
### Using the ID as argument to the same function
|
||||
|
||||
In many cases, the jobs that need to run are the same where the only difference is the arguments for the function. In that case, you can treat the IDs as arguments as they are passed into the Run Function.
|
||||
|
||||
```ts
|
||||
type Id = unknown;
|
||||
type RunFunction = (id: Id) => Promise<unknown>;
|
||||
```
|
||||
|
||||
As you can see, the ID can be anything. It will be passed as the argument for your Run Function. This is a good option if having a large number of functions inside a graph is prohibitive in memory sensitive situations.
|
||||
|
||||
```js
|
||||
const funcs = new Map();
|
||||
const thatImportantTask = (id) => Promise.resolve(id);
|
||||
|
||||
funcs.set("putOnShirt", thatImportantTask);
|
||||
funcs.set("putOnShorts", thatImportantTask);
|
||||
funcs.set("putOnJacket", thatImportantTask);
|
||||
funcs.set("putOnShoes", thatImportantTask);
|
||||
funcs.set("tieShoes", thatImportantTask);
|
||||
```
|
||||
|
||||
## Scopes and filtering
|
||||
|
||||
After a graph are sent to the `pGraph` function, the graph is executed with the `run()` function. The `run()` takes in an argument that lets you filter which tasks to end. This allows you to run tasks up to a certain point in the graph.
|
||||
|
||||
```js
|
||||
// graph is one of the three options up top
|
||||
// depMap is the dependency map where the key is the ID for the Run Function
|
||||
// - the ID CAN be the Run Function itself if graph is specified as the dependency array format
|
||||
await pGraph(graph).run((depMap) => {
|
||||
return [...depMap.keys()].filter((id) => id.startsWith("b"));
|
||||
});
|
||||
```
|
||||
|
||||
# Contributing
|
||||
|
||||
This project welcomes contributions and suggestions. Most contributions require you to agree to a
|
||||
This project welcomes contributions and suggestions. Most contributions require you to agree to a
|
||||
Contributor License Agreement (CLA) declaring that you have the right to, and actually do, grant us
|
||||
the rights to use your contribution. For details, visit https://cla.opensource.microsoft.com.
|
||||
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
module.exports = {
|
||||
preset: "ts-jest",
|
||||
modulePathIgnorePatterns: ["<rootDir>/lib"],
|
||||
testEnvironment: "node",
|
||||
};
|
|
@ -0,0 +1,25 @@
|
|||
{
|
||||
"name": "p-graph",
|
||||
"version": "0.4.0",
|
||||
"license": "MIT",
|
||||
"main": "lib/index.js",
|
||||
"types": "lib/index.d.ts",
|
||||
"scripts": {
|
||||
"build": "tsc",
|
||||
"start": "tsc -w --preserveWatchOutput",
|
||||
"test": "jest",
|
||||
"release": "yarn build && yarn test && beachball publish",
|
||||
"check": "beachball check",
|
||||
"change": "beachball change"
|
||||
},
|
||||
"repository": {
|
||||
"url": "https://github.com/kenotron/p-graph"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/jest": "^25.2.1",
|
||||
"beachball": "^1.31.0",
|
||||
"jest": "^25.5.3",
|
||||
"ts-jest": "^25.4.0",
|
||||
"typescript": "^3.8.3"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,49 @@
|
|||
import { NamedFunctions, DepGraphMap, ScopeFunction, Id } from "./types";
|
||||
|
||||
export class PGraph {
|
||||
private promises: Map<Id, Promise<unknown>> = new Map();
|
||||
|
||||
namedFunctions: NamedFunctions;
|
||||
graph: DepGraphMap;
|
||||
|
||||
constructor(namedFunctions, graph: DepGraphMap) {
|
||||
this.namedFunctions = namedFunctions;
|
||||
this.graph = graph;
|
||||
|
||||
this.promises = new Map();
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs the promise graph with scoping
|
||||
* @param scope
|
||||
*/
|
||||
run(scope?: ScopeFunction) {
|
||||
const scopedPromises = scope
|
||||
? scope(this.graph).map((id) => this.execute(id))
|
||||
: [...this.graph.keys()].map((id) => this.execute(id));
|
||||
|
||||
return Promise.all(scopedPromises);
|
||||
}
|
||||
|
||||
private execute(id: Id) {
|
||||
if (this.promises.has(id)) {
|
||||
return this.promises.get(id);
|
||||
}
|
||||
|
||||
let execPromise: Promise<unknown> = Promise.resolve();
|
||||
|
||||
const deps = this.graph.get(id);
|
||||
|
||||
if (deps) {
|
||||
execPromise = execPromise.then(() =>
|
||||
Promise.all([...deps].map((depId) => this.execute(depId)))
|
||||
);
|
||||
}
|
||||
|
||||
execPromise = execPromise.then(() => this.namedFunctions.get(id)(id));
|
||||
|
||||
this.promises.set(id, execPromise);
|
||||
|
||||
return execPromise;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,42 @@
|
|||
import { PGraph } from "../PGraph";
|
||||
import { NamedFunctions, DepGraphMap } from "../types";
|
||||
|
||||
describe("PGraph", () => {
|
||||
it("should allow a full graph to be created", async () => {
|
||||
const fns: NamedFunctions = new Map();
|
||||
const graph: DepGraphMap = new Map();
|
||||
|
||||
const mockFn = jest.fn((id) => Promise.resolve());
|
||||
|
||||
fns.set("fn1", mockFn);
|
||||
fns.set("fn2", mockFn);
|
||||
|
||||
graph.set("fn1", new Set(["fn2"]));
|
||||
|
||||
const pGraph = new PGraph(fns, graph);
|
||||
await pGraph.run();
|
||||
|
||||
expect(mockFn).toHaveBeenCalledTimes(2);
|
||||
expect(mockFn).toHaveBeenNthCalledWith(1, "fn2");
|
||||
expect(mockFn).toHaveBeenNthCalledWith(2, "fn1");
|
||||
});
|
||||
|
||||
it("should throw when one of the promises threw", async () => {
|
||||
const fns: NamedFunctions = new Map();
|
||||
const graph: DepGraphMap = new Map();
|
||||
|
||||
const mockFn = jest.fn((id) => Promise.resolve());
|
||||
const failFn = jest.fn((id) => {
|
||||
throw new Error("expected failure");
|
||||
});
|
||||
|
||||
fns.set("fn1", mockFn);
|
||||
fns.set("fn2", mockFn);
|
||||
fns.set("fail", failFn);
|
||||
|
||||
graph.set("fn1", new Set(["fn2", "fail"]));
|
||||
|
||||
const pGraph = new PGraph(fns, graph);
|
||||
await pGraph.run();
|
||||
});
|
||||
});
|
|
@ -0,0 +1,78 @@
|
|||
import pGraph from "../index";
|
||||
import { DepGraphArray } from "../types";
|
||||
|
||||
describe("Public API", () => {
|
||||
let calls = [];
|
||||
|
||||
// Example graph from: https://www.npmjs.com/package/toposort
|
||||
const putOnShirt = () =>
|
||||
Promise.resolve("put on your shirt").then((v) => {
|
||||
calls.push(v);
|
||||
});
|
||||
const putOnShorts = () =>
|
||||
Promise.resolve("put on your shorts").then((v) => {
|
||||
calls.push(v);
|
||||
});
|
||||
const putOnJacket = () =>
|
||||
Promise.resolve("put on your jacket").then((v) => {
|
||||
calls.push(v);
|
||||
});
|
||||
const putOnShoes = () =>
|
||||
Promise.resolve("put on your shoes").then((v) => {
|
||||
calls.push(v);
|
||||
});
|
||||
const tieShoes = () =>
|
||||
Promise.resolve("tie your shoes").then((v) => {
|
||||
calls.push(v);
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
calls = [];
|
||||
});
|
||||
|
||||
it("should accept an array dep graph", async () => {
|
||||
const graph: DepGraphArray = [
|
||||
[putOnShoes, tieShoes],
|
||||
[putOnShirt, putOnJacket],
|
||||
[putOnShorts, putOnJacket],
|
||||
[putOnShorts, putOnShoes],
|
||||
];
|
||||
|
||||
await pGraph(graph).run();
|
||||
|
||||
expect(calls).toEqual([
|
||||
"put on your shirt",
|
||||
"put on your shorts",
|
||||
"put on your jacket",
|
||||
"put on your shoes",
|
||||
"tie your shoes",
|
||||
]);
|
||||
});
|
||||
|
||||
it("should accept an array dep graph", async () => {
|
||||
const graph: DepGraphArray = [
|
||||
[putOnShoes, tieShoes],
|
||||
[putOnShirt, putOnJacket],
|
||||
[putOnShorts, putOnJacket],
|
||||
[putOnShorts, putOnShoes],
|
||||
];
|
||||
|
||||
await pGraph(graph).run();
|
||||
|
||||
expect(calls.indexOf("tie your shoes")).toBeGreaterThan(
|
||||
calls.indexOf("put on your shoes")
|
||||
);
|
||||
|
||||
expect(calls.indexOf("put on your jacket")).toBeGreaterThan(
|
||||
calls.indexOf("put on your shirt")
|
||||
);
|
||||
|
||||
expect(calls.indexOf("put on your jacket")).toBeGreaterThan(
|
||||
calls.indexOf("put on your shorts")
|
||||
);
|
||||
|
||||
expect(calls.indexOf("put on your shoes")).toBeGreaterThan(
|
||||
calls.indexOf("put on your shorts")
|
||||
);
|
||||
});
|
||||
});
|
|
@ -0,0 +1,31 @@
|
|||
import {
|
||||
DepGraphArray,
|
||||
NamedFunctions,
|
||||
DepGraphMap,
|
||||
RunFunction,
|
||||
} from "./types";
|
||||
|
||||
export function depArrayToNamedFunctions(array: DepGraphArray) {
|
||||
const namedFunctions: NamedFunctions = new Map();
|
||||
|
||||
// dependant depends on subject (Child depends on Parent means Child is dependent, Parent is subject)
|
||||
for (const [subject, dependent] of array) {
|
||||
namedFunctions.set(subject, subject as RunFunction);
|
||||
namedFunctions.set(dependent, dependent as RunFunction);
|
||||
}
|
||||
return namedFunctions;
|
||||
}
|
||||
|
||||
export function depArrayToMap(array: DepGraphArray) {
|
||||
const graph: DepGraphMap = new Map();
|
||||
|
||||
// dependant depends on subject (Child depends on Parent means Child is dependent, Parent is subject)
|
||||
for (const [subjectId, dependentId] of array) {
|
||||
if (!graph.has(dependentId)) {
|
||||
graph.set(dependentId, new Set([subjectId]));
|
||||
} else {
|
||||
graph.get(dependentId).add(subjectId);
|
||||
}
|
||||
}
|
||||
return graph;
|
||||
}
|
|
@ -0,0 +1,47 @@
|
|||
import { DepGraphArray, NamedFunctions, DepGraphMap } from "./types";
|
||||
import { PGraph } from "./PGraph";
|
||||
import { depArrayToNamedFunctions, depArrayToMap } from "./depConverters";
|
||||
|
||||
function pGraph(namedFunctions: NamedFunctions, graph: DepGraphMap);
|
||||
function pGraph(namedFunctions: NamedFunctions, graph: DepGraphArray);
|
||||
function pGraph(graph: DepGraphArray);
|
||||
|
||||
function pGraph(...args: any[]) {
|
||||
if (args.length < 1 || args.length > 2) {
|
||||
throw new Error("Incorrect number of arguments");
|
||||
}
|
||||
|
||||
let namedFunctions: NamedFunctions;
|
||||
let graph: DepGraphMap;
|
||||
|
||||
if (args.length === 1) {
|
||||
if (!Array.isArray(args[0])) {
|
||||
throw new Error(
|
||||
"Unexpected graph definition format. Expecting graph in the form of [()=>Promise, ()=>Promise][]"
|
||||
);
|
||||
}
|
||||
|
||||
const depArray = args[0] as DepGraphArray;
|
||||
namedFunctions = depArrayToNamedFunctions(depArray);
|
||||
graph = depArrayToMap(depArray);
|
||||
} else if (args.length === 2) {
|
||||
if (Array.isArray(args[0])) {
|
||||
const depArray = args[0] as DepGraphArray;
|
||||
namedFunctions = depArrayToNamedFunctions(depArray);
|
||||
graph = depArrayToMap(depArray);
|
||||
} else if (args[0] instanceof Map && Array.isArray(args[1])) {
|
||||
const depArray = args[1] as DepGraphArray;
|
||||
namedFunctions = args[0];
|
||||
graph = depArrayToMap(depArray);
|
||||
} else if (args[0] instanceof Map && args[1] instanceof Map) {
|
||||
namedFunctions = args[0];
|
||||
graph = args[1];
|
||||
} else {
|
||||
throw new Error("Unexpected arguments");
|
||||
}
|
||||
}
|
||||
|
||||
return new PGraph(namedFunctions, graph);
|
||||
}
|
||||
|
||||
export default pGraph;
|
|
@ -0,0 +1,6 @@
|
|||
export type RunFunction = (id: Id) => Promise<unknown>;
|
||||
export type Id = string | number | RunFunction;
|
||||
export type NamedFunctions = Map<Id, RunFunction>;
|
||||
export type DepGraphMap = Map<Id, Set<Id>>;
|
||||
export type ScopeFunction = (graph: DepGraphMap) => Id[];
|
||||
export type DepGraphArray = [Id, Id][];
|
|
@ -0,0 +1,13 @@
|
|||
{
|
||||
"compilerOptions": {
|
||||
"target": "ES2017",
|
||||
"declaration": true,
|
||||
"module": "CommonJS",
|
||||
"moduleResolution": "node",
|
||||
"esModuleInterop": true,
|
||||
"allowSyntheticDefaultImports": true,
|
||||
"lib": ["ES2017"],
|
||||
"outDir": "lib"
|
||||
},
|
||||
"include": ["src"]
|
||||
}
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
Загрузка…
Ссылка в новой задаче