@boost/core
Version:
Robust pipeline for creating dev tools that separate logic into routines and tasks.
96 lines (95 loc) • 3.43 kB
JavaScript
"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;