batchjs
Version:
Batch processing framework for NodeJS
104 lines (103 loc) • 3.67 kB
JavaScript
import { InternalBufferDuplex } from "../interfaces/_index";
/**
* @class
* Class that allows you to transform and stream data in parallel.
* @extends InternalBufferDuplex
* @template TInput The type of the input data.
* @template TOutput The type of the output data.
* @example
* ```typescript
* const stream:ParallelStream<string,string> = new ParallelStream({
* objectMode: true,
* maxConcurrent: 2,
* transform(chunk: string) {
* return Promise.resolve(chunk.toUpperCase());
* },
* });
*
* stream.write("data1");
* stream.write("data2");
* stream.write("data3");
* stream.end();
*
* stream.on("data", (chunk: string) => {
* console.log(``Pushed chunk: ${chunk}```);
* });
* ```
* ```shell
* >> Pushed chunk: DATA1
* >> Pushed chunk: DATA2
* >> Pushed chunk: DATA3
* ```
*/
export class ParallelStream extends InternalBufferDuplex {
queue = [];
pool = new Set();
maxConcurrent;
transform;
/**
* @constructor
* @param {ParallelStreamOptions<TInput, TOutput>} options - The options for the ParallelStream.
* @param [options.maxConcurrent] {number} - The maximum number of concurrent promises.
* @param [options.transform] {Function} - The function to transform the data returning a promise.
*/
constructor(options) {
super(options);
this.maxConcurrent = options.maxConcurrent;
this.transform = options.transform;
}
/**
* A method to write data to the stream, push the chunk to the queue, transform it, and then execute the callback.
*
* @param {TInput} chunk - The data chunk to write to the stream.
* @param {BufferEncoding} encoding - The encoding of the data.
* @param {TransformCallback} callback - The callback function to be executed after writing the data.
* @return {void} This function does not return anything.
*/
_write(chunk, encoding, callback) {
this.queue.push(chunk);
this._transform();
callback();
}
/**
* Asynchronously finalizes the stream by draining the queue and buffer, pushing any remaining chunks to the stream,
* and calling the provided callback when complete. If the stream is unable to push a chunk, the chunk is placed back
* into the buffer and a PushError is passed to the callback.
*
* @override
* @param {TransformCallback} callback - The callback to be called when the stream is finalized.
* @return {Promise<void>} A promise that resolves when the stream is finalized.
*/
_final(callback) {
const awaitAllProcessed = () => {
if (this.queue.length > 0 || this.pool.size > 0) {
setImmediate(awaitAllProcessed);
}
else {
super._final(callback);
}
};
awaitAllProcessed();
}
/**
* Loop through the pool and queue to process chunks, adding promises to the pool.
*/
_transform() {
while (this.pool.size < this.maxConcurrent && this.queue.length > 0) {
const chunk = this.queue.shift(); // Get the next chunk
const promise = this.transform(chunk)
.then((result) => {
this.buffer.push(result);
this._flush();
})
.catch((err) => {
this.emit("error", err);
})
.finally(() => {
this.pool.delete(promise); // Remove promise from pool
this._transform(); // Continue processing remaining chunks
});
this.pool.add(promise); // Add promise to pool
}
}
}