Skip to content

Commit

Permalink
symbol (#2)
Browse files Browse the repository at this point in the history
  • Loading branch information
tlaziuk authored Jan 20, 2018
1 parent 22d7da7 commit 8de81cd
Show file tree
Hide file tree
Showing 5 changed files with 78 additions and 68 deletions.
7 changes: 4 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,9 @@ There is already a few libraries with similar functionality, yet this is another

| name | description |
| ---: | :--- |
| `ASAP.prototype.concurrency` | the number of tasks to run sumulatenously (1 by default) |
| `ASAP.prototype.enqueue(task)` | enqueue new task, returns a promise which resolves when execution of the task is returned, the task is ma function which returns a value or a promise |
| `ASAP.prototype.c` | the number of tasks to run sumulatenously (1 by default) |
| `ASAP.prototype.q(task)` | enqueue new task, returns a promise which resolves when execution of the task is finished |
| _task_ | task is a function which returns a value or a promise |

## usage

Expand All @@ -45,7 +46,7 @@ import asap from "asap-es";

const queue = new asap();

queue.enqueue(() => Promise.resolve(2)).then(console.log);
queue.q(() => Promise.resolve(2)).then(console.log);
// console >> 2
});
```
44 changes: 22 additions & 22 deletions index.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,22 +25,22 @@ describe(ASAP.name, () => {
});
it("should concurrency be working", () => {
const asap = new ASAP();
expect(() => { asap.concurrency = 1; }).to.not.throw();
expect(() => { asap.concurrency = 0; }).to.throw();
expect(() => { asap.concurrency = -1; }).to.throw();
expect(asap.concurrency).to.be.equal(1);
expect(() => { asap.c = 1; }).to.not.throw();
expect(() => { asap.c = 0; }).to.throw();
expect(() => { asap.c = -1; }).to.throw();
expect(asap.c).to.be.equal(1);
});
it("should the queue run", async () => {
const asap = new ASAP();
const prom = asap.enqueue(() => void 0);
const prom = asap.q(() => void 0);
expect(prom).to.be.instanceof(Promise);
await prom;
});
it("should handle rejection", async () => {
const asap = new ASAP();
await Promise.all([
asap.enqueue(() => { throw new Error(); }),
asap.enqueue(() => Promise.reject(new Error())),
asap.q(() => { throw new Error(); }),
asap.q(() => Promise.reject(new Error())),
].map((prom) => {
prom.catch((e) => {
expect(e).to.be.instanceof(Error);
Expand All @@ -50,42 +50,42 @@ describe(ASAP.name, () => {
it("should the queue run two times", async () => {
const asap = new ASAP();
await Promise.all([
asap.enqueue(() => void 0),
asap.enqueue(() => void 0),
asap.q(() => void 0),
asap.q(() => void 0),
]);
}).timeout(10);
it("should the queue run two slow tasks", async () => {
const asap = new ASAP();
await Promise.all([
asap.enqueue(() => timeout(() => void 0, 50)),
asap.enqueue(() => timeout(() => void 0, 50)),
asap.q(() => timeout(() => void 0, 50)),
asap.q(() => timeout(() => void 0, 50)),
]);
}).timeout(110);
it("should the queue run two slow tasks - concurrency", async () => {
const asap = new ASAP();
asap.concurrency = 2;
asap.c = 2;
await Promise.all([
asap.enqueue(() => timeout(() => void 0, 50)),
asap.enqueue(() => timeout(() => void 0, 50)),
asap.q(() => timeout(() => void 0, 50)),
asap.q(() => timeout(() => void 0, 50)),
]);
}).timeout(60);
it("should the queue run slow tasks with unmatching concurrency to the tasks number", async () => {
const asap = new ASAP();
asap.concurrency = 2;
asap.c = 2;
await Promise.all([
asap.enqueue(() => timeout(() => void 0, 50)),
asap.enqueue(() => timeout(() => void 0, 50)),
asap.enqueue(() => timeout(() => void 0, 50)),
asap.enqueue(() => timeout(() => void 0, 50)),
asap.enqueue(() => timeout(() => void 0, 50)),
asap.q(() => timeout(() => void 0, 50)),
asap.q(() => timeout(() => void 0, 50)),
asap.q(() => timeout(() => void 0, 50)),
asap.q(() => timeout(() => void 0, 50)),
asap.q(() => timeout(() => void 0, 50)),
]);
}).timeout(160);
it("should the queue run when on stress", async () => {
const asap = new ASAP();
asap.concurrency = 10;
asap.c = 10;
await Promise.all(Array(1e4).map(async (v, index) => {
const promSpy = spy();
await asap.enqueue(() => index).then(promSpy, promSpy);
await asap.q(() => index).then(promSpy, promSpy);
expect(promSpy.callCount).to.be.equal(1);
}));
}).timeout(1e4);
Expand Down
88 changes: 50 additions & 38 deletions index.ts
Original file line number Diff line number Diff line change
@@ -1,85 +1,97 @@
// tslint:disable:eofline
export type task<T = any> = () => T | PromiseLike<T>;

export default class ASAP {
/** key for heap of all promises */
const heap = Symbol();
type THeap = Array<() => Promise<any>>;

/** key for process method */
const process = Symbol();
type TProcess = () => void;

/** key for pending promises */
const pending = Symbol();
type TPending = THeap;

/** key for completed promises */
const complete = Symbol();
type TComplete = WeakMap<() => Promise<any>, Promise<any>>;

/** key for completed promises */
const concurrency = Symbol();

export default class {
/**
* array of functions which returns promises
*/
protected queue = [] as Array<() => Promise<any>>;
// @ts-ignore no built-in symbol
protected [heap] = [] as Array<() => Promise<any>>;

/**
* WeakMap of resolved or rejected promises
*/
protected prom = new WeakMap<() => Promise<any>, Promise<any>>();
// @ts-ignore no built-in symbol
protected [complete] = new WeakMap<() => Promise<any>, Promise<any>>();

/**
* array of pending/running promise methods
*/
protected pending = [] as Array<() => Promise<any>>;
// @ts-ignore no built-in symbol
protected [pending] = [] as Array<() => Promise<any>>;

/**
* concurrency protected var
*/
protected c: number = 1;
// @ts-ignore no built-in symbol
protected [concurrency]: number = 1;

/**
* set concurrency
*/
public set concurrency(v: number) {
public set c(v: number) {
if (v < 1) {
throw new Error(`concurrency can not be lower than 1`);
}
this.c = v;
this.process();
(this as any)[concurrency] = v;
(this as any)[process]();
}

/**
* get concurrency
*/
public get concurrency(): number {
return this.c;
public get c(): number {
return (this as any)[concurrency];
}

/**
* enqueue a new task
* @param fn function to run
*/
public enqueue<T>(fn: () => T | PromiseLike<T>): Promise<T> {
public q<T>(fn: task<T>): Promise<T> {
return new Promise<T>((resolve, reject) => {
const promFn = async () => {
const promFn = () => {
const prom = new Promise<T>((result) => { result(fn()); });
prom.then(resolve, reject);
try {
await prom;
} catch {
// pass
} finally {
this.prom.set(promFn, prom);
}
const delFn = () => { (this as any)[complete].set(promFn, prom); };
return prom.then(resolve, reject).then(delFn, delFn);
};
this.queue.push(promFn);
this.process();
(this as any)[heap].push(promFn);
(this as any)[process]();
});
}

/**
* process the queue
*/
public process(): void {
if (this.pending.filter((v) => v).length < this.c) {
this.queue.filter(
(v) => !this.prom.has(v) && this.pending.indexOf(v) < 0,
).slice(0, this.c).map(async (v) => {
this.pending.push(v);
try {
await v();
} catch {
// pass
} finally {
delete this.pending[this.pending.indexOf(v)];
}
protected [process](): void {
if (((this as any)[pending] as TPending).filter((v) => v).length < (this as any)[concurrency]) {
((this as any)[heap] as THeap).filter(
(v) => !(this as any)[complete].has(v) && (this as any)[pending].indexOf(v) < 0,
).slice(0, (this as any)[concurrency]).map((v) => {
(this as any)[pending].push(v);
const delFn = () => { delete (this as any)[pending][(this as any)[pending].indexOf(v)]; };
return v().then(delFn, delFn);
}).forEach((prom) => {
prom.then(() => { this.process(); });
prom.then(() => { (this as any)[process](); });
});
}
}
}
}
5 changes: 1 addition & 4 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "asap-es",
"version": "0.0.1",
"version": "1.0.0",
"description": "asap with promise support",
"main": "index",
"private": false,
Expand Down Expand Up @@ -52,8 +52,5 @@
"ts-node": "^3.1.0",
"tslint": "^5.8.0",
"typescript": "~2.6.0"
},
"dependencies": {
"tslib": "^1.8.0"
}
}
2 changes: 1 addition & 1 deletion tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
"declaration": true,
"emitDecoratorMetadata": true,
"experimentalDecorators": true,
"importHelpers": true,
"importHelpers": false,
"inlineSourceMap": false,
"inlineSources": false,
"lib": [
Expand Down

0 comments on commit 8de81cd

Please sign in to comment.