Skip to content

Commit

Permalink
optimize: 提高了taskQueue的执行效率,减少了重复
Browse files Browse the repository at this point in the history
  • Loading branch information
weixiangmeng521 committed Dec 27, 2023
1 parent efa7231 commit c48d4ba
Show file tree
Hide file tree
Showing 5 changed files with 256 additions and 32 deletions.
85 changes: 85 additions & 0 deletions src/__tests__/task_queue.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
import { TaskQueue, Task, LazyTaskQueue } from '../internal/task_queue';

describe('Task and TaskQueue', () => {
const taskQueue = new TaskQueue();
const lazyTaskQueue = new LazyTaskQueue();

beforeEach(() => {
taskQueue.clear();
lazyTaskQueue.clear();
});

test('Task creation and execution', () => {
const callbackMock = jest.fn();
const task = new Task('Task1', callbackMock);

expect(task.getTaskName()).toBe('Task1');

task.run();
expect(callbackMock).toHaveBeenCalledWith('Task1');
});

test('TaskQueue add and schedule', () => {
const callbackMock = jest.fn();

taskQueue.add('Task1', callbackMock);
taskQueue.schedule('Task1');

expect(callbackMock).toHaveBeenCalledWith('Task1');
});

test('TaskQueue clear', () => {
const callbackMock = jest.fn();

taskQueue.add('Task1', callbackMock);
taskQueue.add('Task2', callbackMock);
taskQueue.clear();

// Use public method to check the length
expect(taskQueue.getQueueLength()).toBe(0);
});

test('TaskQueue multiple tasks with the same name', () => {
const callbackMock1 = jest.fn();
const callbackMock2 = jest.fn();

taskQueue.add('Task1', callbackMock1);
taskQueue.add('Task1', callbackMock2);
taskQueue.schedule('Task1');

expect(callbackMock1).toHaveBeenCalledWith('Task1');
expect(callbackMock2).toHaveBeenCalledWith('Task1');
});

test('LazyTaskQueue multiple tasks with the same name', () => {
const callbackMock1 = jest.fn();
const callbackMock2 = jest.fn();

lazyTaskQueue.add('Task1', callbackMock1);
lazyTaskQueue.add('Task1', callbackMock2);
lazyTaskQueue.schedule('Task1');

expect(callbackMock1).not.toHaveBeenCalled();
expect(callbackMock2).toHaveBeenCalledWith('Task1');
});


test('TaskQueue removing tasks', () => {
const callbackMock1 = jest.fn();
const callbackMock2 = jest.fn();

taskQueue.add('Task1', callbackMock1);
taskQueue.add('Task2', callbackMock2);

taskQueue.schedule('Task1');
expect(callbackMock1).toHaveBeenCalledWith('Task1');
expect(callbackMock2).not.toHaveBeenCalled();

// Use public method to check the length
expect(taskQueue.getQueueLength()).toBe(1);

const _taskQueue = (taskQueue as any);
// Use public method to check if the task was removed
expect(_taskQueue.findTask('Task1')).toBeUndefined();
});
});
11 changes: 6 additions & 5 deletions src/__tests__/teleportSingleton.test.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { TeleportSingleton } from '../index';
import { TaskQueue } from '../internal/task_queue';

describe('TeleportSingleton', () => {
let teleport: TeleportSingleton;
Expand Down Expand Up @@ -62,19 +63,19 @@ describe('TeleportSingleton', () => {
expect(mockHandler2).not.toHaveBeenCalled();
});

test('clear() should clear wait queues and event handlers', () => {
test('clear() should clear task queues and event handlers', () => {
const mockHandler = jest.fn();
teleport.receive('testEvent', mockHandler);

// Use casting to any
const waitQueueMap: Map<string | symbol, ((name: string | symbol) => void)[]> = (teleport as any)._waitQueueMap;
const taskQueue: TaskQueue = (teleport as any)._taskQueue;

expect(waitQueueMap.size).toBeGreaterThan(0);
expect(taskQueue.getQueueLength()).toBeGreaterThan(0);

teleport.clear();

expect(waitQueueMap.size).toBe(0);
expect(teleport['_eventMap'].size).toBe(0); // Direct access to _eventMap without casting
expect(taskQueue.getQueueLength()).toBe(0);
expect(teleport['_taskQueue'].getQueueLength()).toBe(0); // Direct access to _eventMap without casting
});

// Test case for handling multiple events with a common handler
Expand Down
147 changes: 147 additions & 0 deletions src/internal/task_queue.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,147 @@
// Task class represents a task with a name and a callback function
export class Task {
// Private properties
private _name: symbol | string = "";
private _callback: ((name: any) => void) | void;

// Constructor to initialize the task with a name and a callback
constructor(name: string | symbol, callback: ((name: symbol | string) => void)) {
this._name = name;
this._callback = callback;
}

/**
* Get the name of the task.
* @returns {symbol | string} The name of the task.
*/
public getTaskName(): symbol | string {
return this._name;
}

/**
* Execute the task's callback function.
*/
public run(): void {
this._callback?.(this._name);
}
}




// TaskQueue class manages a queue of tasks
export class TaskQueue {
// Private property to store the queue of tasks
private readonly _queue: Task[] = [];

// Constructor to initialize the task queue
constructor() { }

/**
* Find all tasks with a given name.
* @param {string | symbol} name - The name of the tasks to find.
* @returns {Task[]} An array of tasks with the specified name.
*/
protected _findTasksList(name: string | symbol): Task[] {
return this._queue.filter((task: Task) => task.getTaskName() === name);
}

/**
* Remove all tasks with a given name.
* @param {string | symbol} name - The name of the tasks to remove.
*/
protected _removeTasks(name: string | symbol): void {
for (let i = this._queue.length - 1; i >= 0; i--) {
const currentTask = this._queue[i];
if (currentTask && currentTask.getTaskName() === name) {
this._queue.splice(i, 1);
}
}
}

/**
* Find a task with a given name.
* @param {string | symbol} name - The name of the task to find.
* @returns {Task | undefined} The task with the specified name, or undefined if not found.
*/
protected findTask(name: string | symbol): Task | undefined{
return this._queue.find((task: Task) => task.getTaskName() === name);
}


/**
* Find a task with a given name.
* @param {string | symbol} name - The name of the task to find.
* @returns {number} index
*/
protected findTaskIndex(name: string | symbol):number{
return this._queue.findIndex((task: Task) => task.getTaskName() === name);
}

/**
* Add a new task to the queue.
* @param {string | symbol} name - The name of the task.
* @param {((name: symbol | string) => void)} callback - The callback function to execute when the task runs.
*/
public add(name: string | symbol, callback: ((name: symbol | string) => void)): void {
const task = new Task(name, callback);
this._queue.push(task);
}

/**
* Execute the task with a given name and remove all tasks with that name.
* @param {string | symbol} name - The name of the tasks to execute and remove.
*/
public schedule(name: string | symbol): void {
const taskIndex = this.findTaskIndex(name);
const task = this._queue[taskIndex];
if(taskIndex < 0 || !this._queue.length || !task) return;
task.run();
this._queue.splice(taskIndex, 1);
// next
this.schedule(name);
}

/**
* Clear the entire task queue.
*/
public clear(): void {
this._queue.splice(0, this._queue.length);
}


/**
* Get the current length of the task queue.
* @returns {number} The length of the task queue.
*/
public getQueueLength(): number {
return this._queue.length;
}
}





// In order to reduce the number of executions,
// the last execution will overwrite the previous ones and
// only the last task will be executed.
export class LazyTaskQueue extends TaskQueue{


constructor(){
super()
}

/**
* Execute the last task with a given name and remove all tasks with that name.
* @param {string | symbol} name - The name of the tasks to execute and remove.
*/
override schedule(name: string | symbol): void {
const tasksList = this._findTasksList(name);
const finalTask = tasksList[tasksList.length - 1];
finalTask?.run();
this._removeTasks(name);
}

}
42 changes: 16 additions & 26 deletions src/internal/teleport.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
// Import the required RxJS classes and custom types
import { Subject } from "./subject";
import { TaskQueue, LazyTaskQueue } from "./task_queue";
import { EmitDataType } from "./types";
import { isArrayEqual } from "./utils";

Expand All @@ -12,10 +13,12 @@ export class TeleportSingleton {
*/
protected _eventMap: Map<string | symbol, Subject<any>> = new Map();


/**
* Map to store queued event handlers waiting for the event to be created.
* Task queue
*/
protected _waitQueueMap: Map<string | symbol, ((name: string | symbol) => void)[]> = new Map();
protected _taskQueue:LazyTaskQueue = new TaskQueue();


/**
* Singleton instance.
Expand Down Expand Up @@ -205,7 +208,7 @@ export class TeleportSingleton {
if (!subject) {
const _subject = new Subject<any>();
this._eventMap.set(multiEventsName, _subject);
return this._add2WaitMap(multiEventsName, (_name: string | symbol) => {
return this._add2TaskQueue(multiEventsName, (_name: string | symbol) => {
const ptr = this._eventMap.get(_name);
ptr?.next(emitData);
})
Expand All @@ -219,27 +222,18 @@ export class TeleportSingleton {
* @param name - The name or symbol of the event.
* @param handlers - The event handlers to be added to the wait queue.
*/
protected _add2WaitMap(name: string | symbol, ...handlers: ((name: string | symbol) => void)[]): void {
if (this._waitQueueMap.has(name)) {
const queue = this._waitQueueMap.get(name) ?? [];
queue.push(...handlers);
this._waitQueueMap.set(name, queue);
return;
}
this._waitQueueMap.set(name, handlers);
protected _add2TaskQueue(name: string | symbol, ...handlers: ((name: string | symbol) => void)[]): void {
handlers.forEach((handler) => {
this._taskQueue.add(name, handler);
})
}

/**
* Method to execute queued handlers for a specific event.
* @param name - The name or symbol of the event.
*/
protected _fireWaitHandlers(name: string | symbol): void {
const queue = this._waitQueueMap.get(name) ?? [];
while (queue.length) {
const fn = queue.shift();
fn && fn(name);
}
this._waitQueueMap.set(name, []);
protected _fireTaskQueue(name: string | symbol): void {
this._taskQueue.schedule(name);
}

/**
Expand All @@ -261,7 +255,7 @@ export class TeleportSingleton {
if (!subject) {
const _subject = new Subject<EmitDataType<T>>();
this._eventMap.set(name, _subject);
this._add2WaitMap(name, (_name: string | symbol) => {
this._add2TaskQueue(name, (_name: string | symbol) => {
const ptr = this._eventMap.get(_name);
ptr?.next(emitData);
this._afterEmit(name);
Expand Down Expand Up @@ -313,9 +307,7 @@ export class TeleportSingleton {
}

const clearHandler = subject.subscribe({ next: this._eventHandler(handler, 0) });
if ((this._waitQueueMap.get(name) ?? []).length) {
this._fireWaitHandlers(name);
}
this._fireTaskQueue(name);
return clearHandler;
}

Expand Down Expand Up @@ -354,9 +346,7 @@ export class TeleportSingleton {
}

const clearHandler = subject.subscribe({ next: this._eventHandler(handler, 1) });
if ((this._waitQueueMap.get(eventsNameList) ?? []).length) {
this._fireWaitHandlers(eventsNameList);
}
this._fireTaskQueue(eventsNameList);
return clearAll(nameList, clearHandler);
}

Expand Down Expand Up @@ -396,7 +386,7 @@ export class TeleportSingleton {
* Method to clear both wait queues and event handlers.
*/
public clear(): void {
this._waitQueueMap.clear();
this._taskQueue.clear();
this.removeAllHandlers();
this._clearEventCountMap();
this._clearMultiEventsList();
Expand Down
3 changes: 2 additions & 1 deletion tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@
"exclude": [
"node_modules",
"**/__tests__/*",
"./lib/**/*"
"lib",
"index.d.ts"
]
}

0 comments on commit c48d4ba

Please sign in to comment.