scarlet-task
Advanced tools
| import { Scarlet } from './scarlet'; | ||
| /** | ||
| * The task object that is passed to the processor. | ||
| */ | ||
| export declare class TaskObject<T> { | ||
| #private; | ||
| queueId: number; | ||
| task: T; | ||
| hasDone: boolean; | ||
| /** | ||
| * Create a new task object. | ||
| * @param {T} task The task to be processed. | ||
| * @param {number} queueId The id of the queue that the task is in. | ||
| * @param {Scarlet} scarlet The scarlet instance that the task is in. | ||
| */ | ||
| constructor(task: T, queueId: number, scarlet: Scarlet); | ||
| /** | ||
| * Mark the task as done. | ||
| */ | ||
| done(): void; | ||
| } |
| "use strict"; | ||
| var __classPrivateFieldSet = (this && this.__classPrivateFieldSet) || function (receiver, state, value, kind, f) { | ||
| if (kind === "m") throw new TypeError("Private method is not writable"); | ||
| if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a setter"); | ||
| if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot write private member to an object whose class did not declare it"); | ||
| return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value; | ||
| }; | ||
| var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) { | ||
| if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter"); | ||
| if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it"); | ||
| return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver); | ||
| }; | ||
| var _TaskObject_scarlet; | ||
| Object.defineProperty(exports, "__esModule", { value: true }); | ||
| exports.TaskObject = void 0; | ||
| /** | ||
| * The task object that is passed to the processor. | ||
| */ | ||
| class TaskObject { | ||
| /** | ||
| * Create a new task object. | ||
| * @param {T} task The task to be processed. | ||
| * @param {number} queueId The id of the queue that the task is in. | ||
| * @param {Scarlet} scarlet The scarlet instance that the task is in. | ||
| */ | ||
| constructor(task, queueId, scarlet) { | ||
| _TaskObject_scarlet.set(this, void 0); | ||
| this.hasDone = false; | ||
| this.queueId = queueId; | ||
| this.task = task; | ||
| __classPrivateFieldSet(this, _TaskObject_scarlet, scarlet, "f"); | ||
| } | ||
| /** | ||
| * Mark the task as done. | ||
| */ | ||
| done() { | ||
| if (this.hasDone) | ||
| return; | ||
| __classPrivateFieldGet(this, _TaskObject_scarlet, "f").taskDone(this); | ||
| this.hasDone = true; | ||
| } | ||
| } | ||
| exports.TaskObject = TaskObject; | ||
| _TaskObject_scarlet = new WeakMap(); |
+8
-10
| /** | ||
| * Nyaa ヾ(●゜▽゜●)♡ ~ This module name is to remember its | ||
| * initial use for me - to find a touhou project | ||
| * song. | ||
| * Nyaa ヾ(●゜▽゜●)♡ ~ This module name is to remember its initial use for me - | ||
| * to find a Touhou Project song. | ||
| * | ||
| * If no task queue, the website you crawled will | ||
| * be DDOSed by your node.js code. | ||
| * If no task queue, the website you crawled will be DDoSed by your Node.js | ||
| * code. | ||
| * | ||
| * So you need to have a task queue. | ||
| * | ||
| * Otherwise, you can use this queue not only in | ||
| * crawler. | ||
| * Otherwise, you can use this queue not only in crawler. | ||
| * | ||
| * ( ゚∀゚)つ≡≡≡♡♡♡)`ν゜)グシャ | ||
| * | ||
| * -- XadillaX | ||
| * 05/06/2014 | ||
| * -- XadillaX | ||
| * 05/06/2014 | ||
| */ | ||
| export * from './scarlet'; | ||
| export * from './task_object'; | ||
| export * from './task-object'; |
+8
-10
@@ -19,21 +19,19 @@ "use strict"; | ||
| /** | ||
| * Nyaa ヾ(●゜▽゜●)♡ ~ This module name is to remember its | ||
| * initial use for me - to find a touhou project | ||
| * song. | ||
| * Nyaa ヾ(●゜▽゜●)♡ ~ This module name is to remember its initial use for me - | ||
| * to find a Touhou Project song. | ||
| * | ||
| * If no task queue, the website you crawled will | ||
| * be DDOSed by your node.js code. | ||
| * If no task queue, the website you crawled will be DDoSed by your Node.js | ||
| * code. | ||
| * | ||
| * So you need to have a task queue. | ||
| * | ||
| * Otherwise, you can use this queue not only in | ||
| * crawler. | ||
| * Otherwise, you can use this queue not only in crawler. | ||
| * | ||
| * ( ゚∀゚)つ≡≡≡♡♡♡)`ν゜)グシャ | ||
| * | ||
| * -- XadillaX | ||
| * 05/06/2014 | ||
| * -- XadillaX | ||
| * 05/06/2014 | ||
| */ | ||
| /* eslint-enable */ | ||
| __exportStar(require("./scarlet"), exports); | ||
| __exportStar(require("./task_object"), exports); | ||
| __exportStar(require("./task-object"), exports); |
+38
-6
| /// <reference types="node" /> | ||
| import { EventEmitter } from 'events'; | ||
| import { TaskObject } from './task_object'; | ||
| import { TaskObject } from './task-object'; | ||
| export type TaskProcessor<T> = (task: TaskObject<T>) => void | Promise<void>; | ||
| export type AfterFinishProcessor = () => void | Promise<void>; | ||
| interface IQueuingItem<T> { | ||
| queueId: number; | ||
| queueId: number | null; | ||
| task: T; | ||
| processor: TaskProcessor<T>; | ||
| } | ||
| type IQueuingItemArray<T> = IQueuingItem<T>[]; | ||
| /** | ||
| * Scarlet is a task queue that allows you to process tasks in parallel. | ||
| */ | ||
| export declare class Scarlet { | ||
@@ -17,4 +19,5 @@ #private; | ||
| processedCount: number; | ||
| queue: IQueuingItemArray<any>[]; | ||
| spouts: (IQueuingItem<any> | null)[]; | ||
| running: boolean[]; | ||
| queue: IQueuingItem<any>[]; | ||
| emitter: EventEmitter; | ||
@@ -24,10 +27,39 @@ afterFinishCount: number; | ||
| afterFinishProcessor: AfterFinishProcessor | undefined; | ||
| /** | ||
| * Create a new Scarlet instance. | ||
| * @param {number} queueCount The number of queues to create. | ||
| */ | ||
| constructor(queueCount?: number); | ||
| /** | ||
| * Get the number of processed tasks. | ||
| * @return {number} The number of processed tasks. | ||
| */ | ||
| numberOfProcessed(): number; | ||
| /** | ||
| * Reset the number of processed tasks. | ||
| */ | ||
| resetNumberOfProcessed(): void; | ||
| push<T>(task: T, processor: TaskProcessor<T>, debugStr?: boolean): void; | ||
| taskDone<T>(taskObject: TaskObject<T>, debugStr?: boolean): void; | ||
| /** | ||
| * Push a task into the queue. | ||
| * @param {T} task The task to push into the queue. | ||
| * @param {TaskProcessor<T>} processor The processor to run the task. | ||
| */ | ||
| push<T>(task: T, processor: TaskProcessor<T>): void; | ||
| /** | ||
| * Mark a task as done. | ||
| * @param {TaskObject<T>} taskObject The task object to mark as done. | ||
| */ | ||
| taskDone<T>(taskObject: TaskObject<T>): void; | ||
| /** | ||
| * Set the after finish processor. | ||
| * @param {number} count The number of tasks to process before calling the processor. | ||
| * @param {AfterFinishProcessor} processor The processor to call after the tasks are processed. | ||
| * @param {boolean} loop Whether to loop the after finish processor. | ||
| */ | ||
| afterFinish(count: number, processor: AfterFinishProcessor, loop?: boolean): void; | ||
| /** | ||
| * Clear the after finish processor. | ||
| */ | ||
| clearAfterFinish(): void; | ||
| } | ||
| export {}; |
+69
-57
@@ -12,3 +12,3 @@ "use strict"; | ||
| const events_1 = require("events"); | ||
| const task_object_1 = require("./task_object"); | ||
| const task_object_1 = require("./task-object"); | ||
| let asyncRun; | ||
@@ -21,8 +21,16 @@ if (!((_a = globalThis.process) === null || _a === void 0 ? void 0 : _a.nextTick)) { | ||
| } | ||
| /** | ||
| * Scarlet is a task queue that allows you to process tasks in parallel. | ||
| */ | ||
| class Scarlet { | ||
| /** | ||
| * Create a new Scarlet instance. | ||
| * @param {number} queueCount The number of queues to create. | ||
| */ | ||
| constructor(queueCount = 1) { | ||
| _Scarlet_instances.add(this); | ||
| this.processedCount = 0; | ||
| this.spouts = []; | ||
| this.running = []; | ||
| this.queue = []; | ||
| this.running = []; | ||
| this.emitter = new events_1.EventEmitter(); | ||
@@ -33,70 +41,74 @@ this.afterFinishCount = -1; | ||
| for (let i = 0; i < queueCount; i++) { | ||
| this.queue.push([]); | ||
| this.spouts.push(null); | ||
| this.running.push(false); | ||
| } | ||
| this.emitter.on('done', (queueId, debugStr) => { | ||
| this.emitter.on('done', (queueId) => { | ||
| if (queueId >= this.queueCount || queueId < 0) { | ||
| throw new Error(`Invalid queueId, ${queueId} !== ${this.queueCount}`); | ||
| } | ||
| if (debugStr) { | ||
| console.log(`Scarlet done (${this.queue.map(q => q.length).join(', ')})`); | ||
| this.spouts[queueId] = null; | ||
| this.processedCount++; | ||
| // Call the `afterFinishProcessor` if it's set. | ||
| let needRunFinishProcessor; | ||
| if (this.afterFinishLoop) { | ||
| needRunFinishProcessor = !(this.processedCount % this.afterFinishCount); | ||
| } | ||
| asyncRun(() => { | ||
| __classPrivateFieldGet(this, _Scarlet_instances, "m", _Scarlet_runTask).call(this, queueId); | ||
| }); | ||
| else { | ||
| needRunFinishProcessor = this.processedCount === this.afterFinishCount; | ||
| } | ||
| if (needRunFinishProcessor && this.processedCount !== -1 && typeof this.afterFinishProcessor === 'function') { | ||
| this.afterFinishProcessor(); | ||
| if (!this.afterFinishLoop) { | ||
| this.clearAfterFinish(); | ||
| } | ||
| } | ||
| asyncRun(() => __classPrivateFieldGet(this, _Scarlet_instances, "m", _Scarlet_runTask).call(this, queueId)); | ||
| }); | ||
| } | ||
| /** | ||
| * Get the number of processed tasks. | ||
| * @return {number} The number of processed tasks. | ||
| */ | ||
| numberOfProcessed() { | ||
| return this.processedCount; | ||
| } | ||
| /** | ||
| * Reset the number of processed tasks. | ||
| */ | ||
| resetNumberOfProcessed() { | ||
| this.processedCount = 0; | ||
| } | ||
| push(task, processor, debugStr) { | ||
| // Choose a minimun queue | ||
| let min = 0; | ||
| /** | ||
| * Push a task into the queue. | ||
| * @param {T} task The task to push into the queue. | ||
| * @param {TaskProcessor<T>} processor The processor to run the task. | ||
| */ | ||
| push(task, processor) { | ||
| // Push the task into the queue. | ||
| this.queue.push({ queueId: null, task, processor }); | ||
| // Start this task if any queue is stopped. | ||
| for (let i = 0; i < this.queueCount; i++) { | ||
| if (this.queue[i].length < this.queue[min].length) { | ||
| min = i; | ||
| if (!this.running[i]) { | ||
| this.running[i] = true; | ||
| asyncRun(() => __classPrivateFieldGet(this, _Scarlet_instances, "m", _Scarlet_runTask).call(this, i)); | ||
| break; | ||
| } | ||
| } | ||
| // Push the task into the queue | ||
| this.queue[min].push({ | ||
| queueId: min, | ||
| task, | ||
| processor, | ||
| }); | ||
| if (debugStr) { | ||
| console.log(`Scarlet pushed (${this.queue.map(q => q.length).join(', ')})`); | ||
| } | ||
| // Start the queue if it's not running | ||
| if (!this.running[min]) { | ||
| this.running[min] = true; | ||
| asyncRun(() => { | ||
| __classPrivateFieldGet(this, _Scarlet_instances, "m", _Scarlet_runTask).call(this, min); | ||
| }); | ||
| } | ||
| } | ||
| taskDone(taskObject, debugStr) { | ||
| /** | ||
| * Mark a task as done. | ||
| * @param {TaskObject<T>} taskObject The task object to mark as done. | ||
| */ | ||
| taskDone(taskObject) { | ||
| if (taskObject.hasDone) | ||
| return; | ||
| asyncRun(() => { | ||
| this.emitter.emit('done', taskObject.queueId, !!debugStr); | ||
| }); | ||
| this.processedCount++; | ||
| // Call the afterFinishProcessor if it's set | ||
| let needRunFinishProcessor; | ||
| if (this.afterFinishLoop) { | ||
| needRunFinishProcessor = !(this.processedCount % this.afterFinishCount); | ||
| } | ||
| else { | ||
| needRunFinishProcessor = this.processedCount === this.afterFinishCount; | ||
| } | ||
| if (needRunFinishProcessor && this.processedCount !== -1 && typeof this.afterFinishProcessor === 'function') { | ||
| this.afterFinishProcessor(); | ||
| if (!this.afterFinishLoop) { | ||
| this.clearAfterFinish(); | ||
| } | ||
| } | ||
| const queueId = taskObject.queueId; | ||
| asyncRun(() => this.emitter.emit('done', queueId)); | ||
| } | ||
| /** | ||
| * Set the after finish processor. | ||
| * @param {number} count The number of tasks to process before calling the processor. | ||
| * @param {AfterFinishProcessor} processor The processor to call after the tasks are processed. | ||
| * @param {boolean} loop Whether to loop the after finish processor. | ||
| */ | ||
| afterFinish(count, processor, loop) { | ||
@@ -107,2 +119,5 @@ this.afterFinishCount = count; | ||
| } | ||
| /** | ||
| * Clear the after finish processor. | ||
| */ | ||
| clearAfterFinish() { | ||
@@ -116,16 +131,13 @@ this.afterFinishCount = -1; | ||
| _Scarlet_instances = new WeakSet(), _Scarlet_runTask = function _Scarlet_runTask(queueId) { | ||
| const queue = this.queue[queueId]; | ||
| // If empty | ||
| if (queue.length === 0) { | ||
| const task = this.queue.shift(); | ||
| // If task item is not existing, stop the queue. | ||
| if (task === undefined) { | ||
| this.running[queueId] = false; | ||
| return; | ||
| } | ||
| task.queueId = queueId; | ||
| this.running[queueId] = true; | ||
| // Get the task | ||
| const task = queue.shift(); | ||
| if (task.queueId !== queueId) { | ||
| throw new Error(`Invalid queueId, ${task.queueId} !== ${queueId}`); | ||
| } | ||
| this.spouts[queueId] = task; | ||
| const taskObject = new task_object_1.TaskObject(task.task, queueId, this); | ||
| task.processor(taskObject); | ||
| }; |
+3
-3
| { | ||
| "name": "scarlet-task", | ||
| "version": "2.1.0", | ||
| "description": "A task queue module for node.js. You can set several children-queue for one task queue.", | ||
| "version": "3.0.0", | ||
| "description": "An advanced task queue module for Node.js with support for multiple child queues, enabling controlled concurrent processing of asynchronous tasks.", | ||
| "main": "lib/index.js", | ||
@@ -21,3 +21,3 @@ "types": "lib/index.d.ts", | ||
| "scripts": { | ||
| "example": "node example/hackernews.js", | ||
| "example": "node examples/hackernews.js", | ||
| "build": "tsc -p ./tsconfig.build.json", | ||
@@ -24,0 +24,0 @@ "prepublish": "npm run build" |
+17
-11
@@ -5,20 +5,24 @@ # Scarlet Task | ||
| A task queue module for node.js. You can set several children-queue for one task queue. | ||
| Scarlet Task is an advanced task queue module for Node.js, featuring the ability to configure multiple child queues for | ||
| a single task queue. | ||
| ## Why named Scarlet? ๛ก(ー̀ωー́ก) | ||
| At first, I wrote this module is for searching one song in [萌否收音機](https://moe.fm). And last I found that song named <[the Embodiment of Scarlet Devil](https://moe.fm/listen?song=79922)>. | ||
| The story behind this module's name is quite serendipitous. One day, while browsing [萌否收音機](https://moe.fm), I | ||
| stumbled upon a captivating song. Though its title slipped my mind, I distinctly remembered its considerable length. | ||
| For rembembering this and for my favorite [Flandre Scarlet](http://touhou.wikia.com/wiki/Flandre_Scarlet), I named this module `Scarlet Task`. | ||
| Driven by a desire to rediscover this elusive track, I developed this queue module. It allowed me to perform an | ||
| exhaustive search across the entire Moe FM platform. After a thorough exploration, I finally unearthed the song that had | ||
| been haunting my memory: <[the Embodiment of Scarlet Devil](https://moe.fm/listen?song=79922)>. | ||
| ## Usage | ||
| To commemorate this musical treasure hunt and pay homage to my favorite character, | ||
| [Flandre Scarlet](http://touhou.wikia.com/wiki/Flandre_Scarlet), I christened this module 'Scarlet Task'. | ||
| It's a nod to both the journey of rediscovery and the Scarlet-themed song that sparked it all. | ||
| For one situation, once you want to crawl one website. If you use primitive `node.js`, it will like you're DDOSing that website. | ||
| ## Use Case | ||
| So you need a task queue to help you. It will process tasks in queue one by one. | ||
| Scarlet Task is particularly useful for scenarios requiring controlled asynchronous operations, such as web crawling. By | ||
| allowing you to set up multiple child queues, it enables concurrent processing while maintaining control over | ||
| parallelism, preventing issues like unintentional DDoS-like behavior on target websites. | ||
| What's more, you can set that one queue has several children-queue to work concurrently. | ||
| And you can use it at any other situation that suitable. | ||
| ## Installation | ||
@@ -63,3 +67,5 @@ | ||
| > ***Notice:*** In the `processor` function, you should call `taskObject.done()` or `taskQueue.taskDone(taskObject)` when you think this task is done. And then the `taskQueue` will process next task. The parameter `taskObject` is a parameter that `taskQueue` passed to you. | ||
| > ***Notice:*** In the `processor` function, you should call `taskObject.done()` or `taskQueue.taskDone(taskObject)` | ||
| > when you think this task is done. And then the `taskQueue` will process next task. The parameter `taskObject` is a | ||
| > parameter that `taskQueue` passed to you. | ||
@@ -66,0 +72,0 @@ You can push task(s) at anytime. |
Long strings
Supply chain riskContains long string literals, which may be a sign of obfuscated or packed code.
Found 1 instance in 1 package
20094
32.69%11
22.22%361
41.02%129
4.88%1
Infinity%