Skip to content

Commit

Permalink
v1.0.0
Browse files Browse the repository at this point in the history
  • Loading branch information
yoannchb-pro committed Nov 9, 2022
1 parent 9150e11 commit 22de65e
Show file tree
Hide file tree
Showing 10 changed files with 268 additions and 71 deletions.
66 changes: 56 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
# Enqueu

> NOTE: The project documentation still in development and the package is not published yet
Promise queue for concurrency control

## Installation
Expand All @@ -18,27 +16,75 @@ or

## Example

In this case you will get "hey, my name is yoann, how are you ?, bye" and without Enqueu "my name is yoann, bye, how are you ?, hey".

```js
import Enqueu from "enqueu";

const queu = new Enqueu();

queu.add(() => fetch("/home"));
queu.add(() => fetch("/about"));
queu.add(() => fetch("/enqueu"));
queu
.add(() => new Promise((r) => setTimeout(r, 3000)))
.then((_) => console.log("hey"));

queu
.add(() => new Promise((r) => setTimeout(r, 2000)))
.then((_) => console.log("how are you ?"));

queu
.add(() => new Promise((r) => setTimeout(r, 1000)))
.then((_) => console.log("bye"));

queu
.add(() => new Promise((r) => setTimeout(r, 500)), { priority: 1 }) //from 1 to Infinity
.then((_) => console.log("my name is Yoann"));
```

With event listener
### With event listener

For example in this case if you click 4 times at once on the button you will get "10, 5, 2.5".
But without Enqueu you will get "1.25, 1.25, 1.25, 1.25".

```js
const queu = new Enqueu(3); //the maximum size of the queu is 3 (other will be throwed off)
const queu = new Enqueu({ maxSize: 3 }); //the maximum size of the queu is 3 (other will be throwed off)

let count = 20;

document.querySelector("button").addEventListener(
"click",
queu.createFn(function () {
return fetch("/add-article").then(() => console.log("added"));
count = count / 2;
return new Promise((r) =>
setTimeout(function () {
r(count);
}, count * 1000)
);
})
);

//if we click 5 times we will get added, added, added (each function we will wait the other one finished)
```

## Enqueu

### Constructor

- `maxSize` Max size of the queu
- `maxConcurrency` Max promise as the same time

### Attibutes

- `isPaused` Return a boolean to see if the queu is paused or not
- `pending` Return the number of pending functions
- `queu` Return the queu

### Methods

- `add(fn: Function, options: Options = {}): Function` Add the function to the queu
- `createFn(fn: Function, options: Options = {}): Function` Create a function that will add "fn" to the queu on call (useful for addEventListener for example)
- `pause()` Pause the queu
- `start()` Start the queu after a pause
- `onEmpty(fn: Function)` Call the function passed as argument when the queu is empty
- `onQueuElementExecuted(fn: Function)` Call the function passed as argument when the a new queu function is started
- `onQueuElementFinishExecution(fn: Function)` Call the function passed as argument when the a new queu function which started is finished
- `clear()` Clear the queu
- `remove(fn: Function)` Remove a function from the queu
- `removeFromIndex(index: number)` Remove a specified indexed element in the queu
27 changes: 22 additions & 5 deletions dist/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,22 +2,39 @@ type Queu = {
fn: Function;
arguments: any;
priority: number;
resolve: Function;
reject: Function;
started?: boolean;
};
type Options = Partial<{
priority: number;
execute: boolean;
}>;
declare class Enqueu {
type OptionsConstructor = Partial<{
maxSize: number;
maxConcurrency: number;
}>;
declare class Enqueu {
private _queu;
private _paused;
private _emptyFunction;
private _executedFunction;
private _executionFinishFunction;
constructor(maxSize?: number);
private options;
constructor(options?: OptionsConstructor);
get isPaused(): boolean;
get pending(): number;
get queu(): Queu[];
/**
* Return important elements of queu element
* @param queuElement
* @returns
*/
/**
* Return important elements of queu element
* @param queuElement
* @returns
*/
private computeQueuElement;
/**
* Start the next element from the queu
*/
Expand All @@ -35,14 +52,14 @@ declare class Enqueu {
* @param fn
* @param Options
*/
add(fn: Function, options?: Options): void;
add(fn: Function, options?: Options): Promise<unknown>;
/**
* Create a function that register other function in the queu
*/
/**
* Create a function that register other function in the queu
*/
createFn(fn: Function, options?: Options): () => Promise<void>;
createFn(fn: Function, options?: Options): () => Promise<unknown>;
/**
* Remove element from the queu
* @param fn
Expand Down
68 changes: 50 additions & 18 deletions dist/index.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion dist/index.js.map

Large diffs are not rendered by default.

92 changes: 70 additions & 22 deletions index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,18 @@ type Queu = {
fn: Function;
arguments: any;
priority: number;
resolve: Function;
reject: Function;
started?: boolean;
};

type Options = Partial<{
priority: number;
execute: boolean;
}>;

type OptionsConstructor = Partial<{
maxSize: number;
maxConcurrency: number;
}>;

class Enqueu {
Expand All @@ -17,7 +24,11 @@ class Enqueu {
private _executedFunction: Function;
private _executionFinishFunction: Function;

constructor(public maxSize: number = Infinity) {}
private options: OptionsConstructor = {};

constructor(options: OptionsConstructor = {}) {
Object.assign(this.options, { maxConcurrency: 1 }, options);
}

get isPaused() {
return this._paused;
Expand All @@ -31,32 +42,57 @@ class Enqueu {
return this._queu;
}

/**
* Return important elements of queu element
* @param queuElement
* @returns
*/
private computeQueuElement(queuElement: Queu) {
return {
fn: queuElement.fn,
arguments: queuElement.arguments,
priority: queuElement.priority,
};
}

/**
* Start the next element from the queu
*/
private async startNextQueuElement() {
const actual = this._queu[0];
private startNextQueuElement() {
const actual = this._queu.find((e) => !e.started);

if (this._executedFunction) this._executedFunction(actual);
if (!actual) return;

await actual.fn(...actual.arguments);
actual.started = true;

this._queu.shift(); //after execution we remove it
if (this._executedFunction)
this._executedFunction(this.computeQueuElement(actual));

if (this._executionFinishFunction) this._executionFinishFunction(actual);
actual
.fn(...actual.arguments)
.then(actual.resolve)
.catch(actual.reject)
.finally(() => {
//after execution we remove it
const index = this._queu.findIndex((e) => e === actual);
this._queu.splice(index, 1);

if (this._queu.length > 0) this.startNextQueuElement();
else if (this._emptyFunction) this._emptyFunction();
if (this._executionFinishFunction)
this._executionFinishFunction(this.computeQueuElement(actual));

if (this._queu.length > 0) this.startNextQueuElement();
else if (this._emptyFunction) this._emptyFunction();
});
}

/**
* Same as createFn but directly add the function to the queu
* @param fn
* @param Options
*/
public add(fn: Function, options: Options = {}) {
public async add(fn: Function, options: Options = {}) {
const queuFn = this.createFn(fn, options);
queuFn();
return await queuFn();
}

/**
Expand All @@ -65,16 +101,28 @@ class Enqueu {
public createFn(fn: Function, options: Options = {}) {
const obj = this; //no function arrow because we need access to "arguments"

return async function () {
if (obj._queu.length === obj.maxSize) return; //if max size

obj._queu.push({ fn, arguments, priority: options.priority ?? 0 }); // adding to the queu
if (options.priority) obj._queu.sort((a, b) => a.priority - b.priority); //sorting by priority

//starting the function
if ((obj._queu.length === 1 && !obj._paused) || options.execute) {
await obj.startNextQueuElement();
}
return function () {
return new Promise((resolve, reject) => {
if (obj._queu.length === obj.options.maxSize) return; //if max size

// adding to the queu
const queuElement = {
fn,
arguments,
priority: options.priority ?? Infinity,
resolve,
reject,
};
obj._queu.push(queuElement);

//sorting by priority
if (options.priority) obj._queu.sort((a, b) => a.priority - b.priority);

//starting the function
if (obj._queu.length <= obj.options.maxConcurrency && !obj._paused) {
obj.startNextQueuElement();
}
});
};
}

Expand Down

0 comments on commit 22de65e

Please sign in to comment.