JS – Queue Manager

A continuación expongo una solución para solventar la problemática de ejecutar múltiples peticiones simultaneas pero pidiendo controlar que cantidad de concurrencias hay,

De esta forma podremos crear un pequeño objecto que permite gestionar la cola automáticamente y con eventos que indica cuando se ha finalizado un proceso o todo el lote de el

type typeFinishJobFunc = {
    uuid: number,
    status: string,
    job: any,
    jobPromise: any
}
type typeStartJobFunc = {
    uuid: number,
    job: any,
    jobPromise: any
}
type typeJob = {
    task: () => Promise<unknown>
}
class QueueManager {
    uuid: number
    jobs: typeJob[]
    concurrentJobs: number
    status: string
    executingJobs: any[]
    finishedJobs: any[]
    errorJobs: any[]
    stoppingFunc?: (isAlreadyStopped: boolean) => void
    stopedFunc?: () => void
    finishedFunc?: () => void
    updateFunc?: () => void
    startJobFunc?: (object: typeStartJobFunc) => void
    finishJobFunc?: (object: typeFinishJobFunc) => void
    constructor() {
        this.uuid = 0
        this.jobs = []
        this.concurrentJobs = 8
        this.status = 'stopped'
        this.executingJobs = []
        this.finishedJobs = []
        this.errorJobs = []
    }
    executeJobs(jobs: typeJob[]) {
        if (this.status !== 'stopped') return false;
        this.uuid = 0
        this.status = 'processing'
        this.executingJobs = []
        this.finishedJobs = []
        this.errorJobs = []
        this.jobs = jobs
        this.update();
        for (let i = 0; i < this.concurrentJobs; i++) {
            if (this.jobs.length > 0) {
                this.startQueue()
            }
        }
        return true;
    }
    update() {
        if (this.updateFunc) {
            this.updateFunc()
        }
    }
    startQueue() {
        const job = this.jobs.shift()
        if (job) {
            const uuid = this.uuid
            this.uuid++
            const jobPromise = job.task()
                .then(() => {
                    this.finishedJobs.push(job)
                    if (this.finishJobFunc) {
                        this.finishJobFunc({
                            uuid,
                            status: 'success',
                            job,
                            jobPromise
                        })
                    }
                })
                .catch(() => {
                    this.errorJobs.push(job)
                    if (this.finishJobFunc) {
                        this.finishJobFunc({
                            uuid,
                            status: 'error',
                            job,
                            jobPromise
                        })
                    }
                })
                .finally(() => {
                    const indexUuid = this.executingJobs.findIndex(j => j.uuid === uuid)
                    this.executingJobs.splice(indexUuid, 1)
                    this.update()
                    if (this.status === 'processing') {
                        this.startQueue()
                        return
                    }
                })
            this.executingJobs.push({
                uuid,
                job,
                jobPromise
            })
            this.update()
            if (this.startJobFunc) {
                this.startJobFunc({
                    uuid,
                    job,
                    jobPromise
                })
            }
        } else {
            if (this.executingJobs.length === 0) {
                this.status = 'stopped'
                this.update();
                if (this.finishedFunc) {
                    this.finishedFunc();
                }
            }
        }
    }
    stop() {
        if (this.status === 'procesing-stop') {
            if (this.stoppingFunc) {
                this.stoppingFunc(false);
            }
            return false;
        }
        this.status = 'procesing-stop'
        this.update()
        if (this.stoppingFunc) {
            this.stoppingFunc(true);
        }
        Promise.allSettled(this.executingJobs.map(p => p.jobPromise))
            .then(() => {
                this.status = 'stopped'
                this.update()
                if (this.stopedFunc) {
                    this.stopedFunc();
                }
            })
    }
    getAllJobs() {
        return this.executingJobs.length + this.finishedJobs.length + this.errorJobs.length + this.jobs.length
    }
    getCurrentFinishedJobs() {
        return this.finishedJobs.length + this.errorJobs.length
    }
}
export {
    QueueManager
}

La forma de uso:

const queueManager = new QueueManager();
queueManager.concurrentJobs = 1;
queueManager.startJobFunc = () => console.log('Job started');
queueManager.finishedFunc = () => console.log('job finished');
queueManager.stoppingFunc = () => console.log('Stopping queues')
queueManager.stopedFunc = () => console.log('job stopped');
queueManager.updateFunc = () => console.log('job updated');

const jobs = [];

fileNames.forEach(fileName => jobs.push({
    fileName: fileName,
    task: () => {
        return new Promise((resolve, reject) => {
            console.log('do_stuff')
        })
    }
}));

queueManager.executeJobs(jobs);

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *

Este sitio usa Akismet para reducir el spam. Aprende cómo se procesan los datos de tus comentarios.