134 lines
3.6 KiB
JavaScript
134 lines
3.6 KiB
JavaScript
"use strict";
|
|
// Copyright (c) Microsoft Corporation.
|
|
// Licensed under the MIT License.
|
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
exports.Batch = void 0;
|
|
// In browser, during webpack or browserify bundling, this module will be replaced by 'events'
|
|
// https://github.com/Gozala/events
|
|
const events_1 = require("events");
|
|
/**
|
|
* States for Batch.
|
|
*/
|
|
var BatchStates;
|
|
(function (BatchStates) {
|
|
BatchStates[BatchStates["Good"] = 0] = "Good";
|
|
BatchStates[BatchStates["Error"] = 1] = "Error";
|
|
})(BatchStates || (BatchStates = {}));
|
|
/**
|
|
* Batch provides basic parallel execution with concurrency limits.
|
|
* Will stop execute left operations when one of the executed operation throws an error.
|
|
* But Batch cannot cancel ongoing operations, you need to cancel them by yourself.
|
|
*/
|
|
class Batch {
|
|
/**
|
|
* Concurrency. Must be lager than 0.
|
|
*/
|
|
concurrency;
|
|
/**
|
|
* Number of active operations under execution.
|
|
*/
|
|
actives = 0;
|
|
/**
|
|
* Number of completed operations under execution.
|
|
*/
|
|
completed = 0;
|
|
/**
|
|
* Offset of next operation to be executed.
|
|
*/
|
|
offset = 0;
|
|
/**
|
|
* Operation array to be executed.
|
|
*/
|
|
operations = [];
|
|
/**
|
|
* States of Batch. When an error happens, state will turn into error.
|
|
* Batch will stop execute left operations.
|
|
*/
|
|
state = BatchStates.Good;
|
|
/**
|
|
* A private emitter used to pass events inside this class.
|
|
*/
|
|
emitter;
|
|
/**
|
|
* Creates an instance of Batch.
|
|
* @param concurrency -
|
|
*/
|
|
constructor(concurrency = 5) {
|
|
if (concurrency < 1) {
|
|
throw new RangeError("concurrency must be larger than 0");
|
|
}
|
|
this.concurrency = concurrency;
|
|
this.emitter = new events_1.EventEmitter();
|
|
}
|
|
/**
|
|
* Add a operation into queue.
|
|
*
|
|
* @param operation -
|
|
*/
|
|
addOperation(operation) {
|
|
this.operations.push(async () => {
|
|
try {
|
|
this.actives++;
|
|
await operation();
|
|
this.actives--;
|
|
this.completed++;
|
|
this.parallelExecute();
|
|
}
|
|
catch (error) {
|
|
this.emitter.emit("error", error);
|
|
}
|
|
});
|
|
}
|
|
/**
|
|
* Start execute operations in the queue.
|
|
*
|
|
*/
|
|
async do() {
|
|
if (this.operations.length === 0) {
|
|
return Promise.resolve();
|
|
}
|
|
this.parallelExecute();
|
|
return new Promise((resolve, reject) => {
|
|
this.emitter.on("finish", resolve);
|
|
this.emitter.on("error", (error) => {
|
|
this.state = BatchStates.Error;
|
|
reject(error);
|
|
});
|
|
});
|
|
}
|
|
/**
|
|
* Get next operation to be executed. Return null when reaching ends.
|
|
*
|
|
*/
|
|
nextOperation() {
|
|
if (this.offset < this.operations.length) {
|
|
return this.operations[this.offset++];
|
|
}
|
|
return null;
|
|
}
|
|
/**
|
|
* Start execute operations. One one the most important difference between
|
|
* this method with do() is that do() wraps as an sync method.
|
|
*
|
|
*/
|
|
parallelExecute() {
|
|
if (this.state === BatchStates.Error) {
|
|
return;
|
|
}
|
|
if (this.completed >= this.operations.length) {
|
|
this.emitter.emit("finish");
|
|
return;
|
|
}
|
|
while (this.actives < this.concurrency) {
|
|
const operation = this.nextOperation();
|
|
if (operation) {
|
|
operation();
|
|
}
|
|
else {
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
exports.Batch = Batch;
|
|
//# sourceMappingURL=Batch.js.map
|