Skip to content

Commit

Permalink
feat: pauseQueue config option + methods
Browse files Browse the repository at this point in the history
  • Loading branch information
hemant-fundwave committed Dec 11, 2023
1 parent 0e1fdf3 commit 1b815e3
Show file tree
Hide file tree
Showing 5 changed files with 75 additions and 26 deletions.
1 change: 1 addition & 0 deletions jest.config.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
export default {
preset: "ts-jest",
testEnvironment: "node",
fakeTimers: { enableGlobally: true },
transform: {
"^.+\\.(ts|tsx)$": "ts-jest",
},
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
"build:esm": "tsc --module NodeNext --outDir ./dist/esm",
"build": "tsc --module commonjs --outDir ./dist/cjs",
"prepare": "npm run build && npm run build:esm",
"test": "jest",
"test": "jest --detectOpenHandles",
"lint": "npx eslint src/index.ts",
"docs": "npx jsdoc-to-markdown ./src/index.ts --configure ./jsdoc2md.json > DOCUMENTATION.md"
},
Expand Down
46 changes: 33 additions & 13 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,11 @@ export class FetchQueue {
*/
private _queue: Array<() => void>;

/**
* If true, Disables task executions but {@link _queue} gets populated.
*/
private _pauseQueue: boolean;

/**
* Initializes a new instance of the FetchQueue class with an optional FetchQueueConfig object.
* If no options are provided, the default concurrent value is set to 3.
Expand All @@ -47,6 +52,7 @@ export class FetchQueue {
this._queue = [];
this._urlsQueued = [];
this._urlsExecuting = [];
this._pauseQueue = options?.pauseQueueOnInit || false;

if (typeof this._concurrent !== "number" || this._concurrent <= 0) {
throw new Error("Concurrent should be a number greater than zero.");
Expand All @@ -61,10 +67,7 @@ export class FetchQueue {
* @param options - The options for the fetch request.
* @returns A Promise that resolves to the fetch response.
*/
private _run = async (
url: URL | RequestInfo,
options?: RequestInit
): Promise<Response> => {
private _run = async (url: URL | RequestInfo, options?: RequestInit): Promise<Response> => {
this._activeRequests++;
try {
if (this._debug) {
Expand Down Expand Up @@ -93,7 +96,7 @@ export class FetchQueue {
this._urlsQueued.shift();
}
}
if (this._queue.length <= 0) return;
if (this._queue.length <= 0 || this._pauseQueue) return;
const nextTask = this._queue.shift();
nextTask!();
};
Expand Down Expand Up @@ -136,6 +139,28 @@ export class FetchQueue {
this._debug = debug;
}

/**
* Disables the queuing of fetch requests in the FetchQueue.
* Sets the `_disableQueue` property to true and the `_activeRequests` property to 0.
*
* @returns {void}
*/
public pauseQueue(): void {
this._pauseQueue = true;
this._activeRequests = 0;
}

/**
* Enables the queuing of fetch requests in the FetchQueue.
* Sets the `_disableQueue` property to false and calls the `_emitRequestCompletedEvent` method.
*
* @returns {void}
*/
public startQueue(): void {
this._pauseQueue = false;
this._emitRequestCompletedEvent();
}

/**
* @returns Length of queue
*/
Expand All @@ -147,13 +172,10 @@ export class FetchQueue {
* The internal fetch implementation that handles queuing of fetch requests.
*/
private _f_fetch = (() => {
return (
url: RequestInfo | URL,
options?: RequestInit
): Promise<Response> => {
return (url: RequestInfo | URL, options?: RequestInit): Promise<Response> => {
const task = () => this._run(url, options);

if (this._activeRequests < this._concurrent) {
if (this._activeRequests < this._concurrent && !this._pauseQueue) {
return task();
} else {
return new Promise((resolve, reject) => {
Expand All @@ -162,9 +184,7 @@ export class FetchQueue {
};
this._queue.push(queueTask);
if (this._debug) {
this._urlsQueued.push(
url.toString().split("/").slice(-3).join("/")
);
this._urlsQueued.push(url.toString().split("/").slice(-3).join("/"));
}
});
}
Expand Down
1 change: 1 addition & 0 deletions src/interfaces/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
export type FetchQueueConfig = {
concurrent: number;
pauseQueueOnInit?: boolean
debug?: boolean;
};
51 changes: 39 additions & 12 deletions tests/index.test.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,6 @@
import { FetchQueue } from "../src/index";

const urls = [
"https://example.com/",
"https://github.com/",
"https://example.com/3",
"https://google.com/",
];
const urls = ["https://example.com/", "https://github.com/", "https://example.com/3", "https://google.com/"];

describe("FetchQueue", () => {
// test
Expand All @@ -15,21 +10,20 @@ describe("FetchQueue", () => {

// test
it("should execute multiple fetch requests with expected queue lengths", async () => {
jest.useFakeTimers();
const fetchQueue = new FetchQueue({ concurrent: 2 });
const fetchQueue = new FetchQueue({ concurrent: 1 });

const fetch = fetchQueue.getFetchMethod();
const mockFetch = jest.fn().mockImplementation(async (url, urlIndex) => {
jest.advanceTimersByTime(5000);
switch (urlIndex) {
case 0:
case 1:
case 2:
expect(fetchQueue.getQueueLength()).toBe(0);
break;
case 3:
case 2:
expect(fetchQueue.getQueueLength()).toBe(1);
break;
case 4:
case 3:
expect(fetchQueue.getQueueLength()).toBe(2);
break;
}
Expand Down Expand Up @@ -101,7 +95,40 @@ describe("FetchQueue", () => {
}, 60000);

afterEach(() => {
jest.useRealTimers();
jest.clearAllMocks();
});
});

describe("FetchQueue with configuration", () => {
//test
it("should not execute fetch request with disableQueue true in config", async () => {
const fetchQueue = new FetchQueue({ concurrent: 2, pauseQueueOnInit: true });
const fetch = fetchQueue.getFetchMethod();

const mockFetch = jest.fn().mockImplementation(async (url, urlIndex) => {
fetch(url);
jest.advanceTimersByTime(5000);
switch (urlIndex) {
case 0:
case 1:
expect(fetchQueue["_activeRequests"]).toBe(0);
break;
case 2:
expect(fetchQueue["_activeRequests"]).toBe(0);
expect(fetchQueue.getQueueLength()).toBe(3);
break;
case 3:
expect(fetchQueue["_activeRequests"]).toBe(0);
expect(fetchQueue.getQueueLength()).toBe(4);
break;
}
return Promise.resolve(true);
});

const promises = urls.map((url) => mockFetch(url, urls.indexOf(url)));
await Promise.all(promises);

fetchQueue.startQueue();
expect(fetchQueue.getQueueLength()).toBe(3);
});
});

0 comments on commit 1b815e3

Please sign in to comment.