UNPKG

@boost/core

Version:

Robust pipeline for creating dev tools that separate logic into routines and tasks.

96 lines (95 loc) 3.43 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; var __importStar = (this && this.__importStar) || function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k in mod) if (Object.hasOwnProperty.call(mod, k)) result[k] = mod[k]; result["default"] = mod; return result; }; Object.defineProperty(exports, "__esModule", { value: true }); const os_1 = __importDefault(require("os")); const optimal_1 = __importStar(require("optimal")); const Executor_1 = __importDefault(require("../Executor")); class PoolExecutor extends Executor_1.default { constructor(tool, context, options) { super(tool, context, options); this.handler = null; this.parallel = true; this.queue = []; this.resolver = null; this.results = []; this.running = []; this.options = optimal_1.default(Object.assign({}, options), { concurrency: optimal_1.number(os_1.default.cpus().length).gte(1), fifo: optimal_1.bool(true), timeout: optimal_1.number(0).gte(0), }, { name: 'PoolExecutor', }); } /** * Execute tasks using a pool with a max concurrency. */ run(handler, tasks, value) { if (tasks.length === 0) { return Promise.resolve(this.aggregateResponse([])); } const { concurrency, timeout } = this.options; this.handler = handler; this.debug('Pooling %d tasks', tasks.length); return new Promise(resolve => { this.queue = [...tasks]; // Break references this.resolver = resolve; // eslint-disable-next-line promise/catch-or-return Promise.all(this.queue.slice(0, concurrency).map(() => this.runItem(value))); if (timeout) { this.timeoutTimer = setTimeout(() => this.resolve(), timeout); } }); } /** * Resolve the execution with the current results. */ resolve() { if (this.resolver) { this.resolver(this.aggregateResponse(this.results)); } if (this.timeoutTimer) { clearTimeout(this.timeoutTimer); } } /** * Run a task from the queue, and start the next task one it passes or fails. */ runItem(value) { const task = this.options.fifo ? this.queue.shift() : this.queue.pop(); if (!task || !this.handler) { return Promise.resolve(); } this.running.push(task); const handleResult = (result) => { this.running = this.running.filter(running => running !== task); this.results.push(result); this.nextItem(value); }; return this.handler(task, value) .then(handleResult) .catch(handleResult); } /** * Run the next task if there are tasks available in the queue, and the max concurrency isn't met. * Otherwise, resolve and exit the current pool. */ nextItem(value) { if (this.queue.length > 0 && this.running.length < this.options.concurrency) { this.runItem(value); } else if (this.queue.length === 0 && this.running.length === 0) { this.resolve(); } } } exports.default = PoolExecutor;