array-chunk-reader
Version:
Module for reading array by chunks using promise
158 lines (128 loc) • 4.09 kB
JavaScript
(function () {
"use strict";
class ArrayChunkReader {
constructor(array, options) {
if(!Array.isArray(array)) {
throw new Error('You have to pass an array');
}
options = options || {};
this.array = array;
this.arrayLength = array.length;
this.startFrom = options.startFrom || options.from || 0;
this.startTo = options.readTo || options.to || this.arrayLength;
if (this.startFrom > array.length) {
this.startFrom = length;
}
if(this.startTo < this.startFrom) {
throw new Error('Wrong range for reading');
}
this.currentFrom = this.startFrom;
this.currentTo = this.startTo;
const defaults = {
size: Math.floor(Math.sqrt(this.arrayLength)),
timeout: 1,
log: true
};
this.options = Object.assign(defaults, options || {});
}
getCurrentChunkSize() {
const diff = this.currentTo - this.currentFrom;
if(diff < this.options.size) {
return diff;
}
return this.options.size;
}
chunk(lastInfo, fn, fnChunk, callback) {
lastInfo = lastInfo || {};
let startChunk = Date.now();
let length = this.startTo;
let start = this.startFrom;
const info = {
iteration: (lastInfo.iteration || 0) + 1,
length: length,
from: lastInfo.to || start,
};
info.to = info.from + this.options.size;
if (info.needTime < 0) {
info.needTime = 0;
}
if (info.to > length) {
info.to = length;
}
if (info.from >= length || this.__stopped) {
let result = {};
result.avgChunkTime = lastInfo.avgTime || 0;
result.chunkCount = lastInfo.iteration || 0;
this.__stopped && this.options.log && console.log("Array chunk reader has been stopped");
return callback(null, info, result);
}
this.currentFrom = info.from;
this.currentTo = info.to;
let promise = [];
for (let i = info.from; i < info.to; i++) {
try {
promise.push(fn(this.array[i], info, lastInfo));
}
catch (err) {
return callback(err);
}
}
Promise.all(promise).then((data) => {
if(!this.__stopped && fnChunk) {
return fnChunk(data);
}
}).then(() => {
let realLength = length - start;
promise = null;
info.time = Date.now() - startChunk;
info.pastTime = (lastInfo.pastTime || 0) + info.time;
info.avgTime = Math.floor(info.pastTime / info.iteration);
info.needTime = Math.floor(realLength * info.avgTime / this.options.size) - info.pastTime;
callback(null, info);
}).catch(callback);
}
start(fn, fnChunk) {
let start = Date.now();
const next = (lastInfo, fn, fnChunk, callback) => {
this.chunk(lastInfo, fn, fnChunk, (err, info, result) => {
if (err) {
return callback(err);
}
else if (result) {
result.time = Date.now() - start;
return callback(null, result);
}
if (this.options.log) {
console.log("chunk:", JSON.stringify(info, undefined, 1));
}
if (this.options.timeout == 0) {
return next(info, fn, fnChunk, callback);
}
setTimeout(() => {
next(info, fn, fnChunk, callback);
}, this.options.timeout);
});
};
return new Promise((res, rej) => {
next(null, fn, fnChunk, (err, result) => {
if (err) {
return rej(err);
}
else if (result) {
return res(result);
}
rej(new Error("Array chunk reader is failed"));
});
});
}
stop() {
this.__stopped = true;
}
}
if (typeof module == "object" && typeof module.exports == "object") {
module.exports = ArrayChunkReader;
}
else if (typeof window == "object") {
window.ArrayChunkReader = ArrayChunkReader;
}
})();