UNPKG

seq-prom

Version:

A small library to allow for sequential operations on an array using Promises

237 lines (190 loc) 7.18 kB
"use strict"; require("core-js/modules/web.dom.iterable"); require("core-js/modules/es6.array.iterator"); require("core-js/modules/es6.string.iterator"); require("core-js/modules/es6.promise"); require("core-js/modules/es6.regexp.to-string"); require("core-js/modules/es6.regexp.match"); function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } function _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); return Constructor; } /** * Created by adrianbrowning */ var blankFunction = function blankFunction() {}; function isTrue(value) { return value === true || (value || '').toLowerCase() === 'true'; } function typeOf(e) { return {}.toString.call(e).match(/\s([a-zA-Z]+)/)[1].toLowerCase(); } function trackResponse(response) { this._responses.push(response); } function errorCallBack(self, item) { return function (reason) { self._errors.push({ item: item, reason: reason }); self.errorCB(item, reason); }; } function buildBatchQueue(self) { function secondary(item) { return new Promise(function (resolve, reject) { return self.cb(item, resolve, reject, self); }).then(trackResponse.bind(self)).catch(errorCallBack(self, item)); } var createAllPromise = function createAllPromise(items) { var pList = []; for (var i = 0; i < items.length; i++) { pList.push(secondary(items[i])); } return Promise.all(pList); }; var nextBatch = function nextBatch(_start, _size) { _start = _start || 0; _size = _size || self.batchSize; if (self._stopped) return; return new Promise(function (res) { return createAllPromise(self.list.splice(_start, _size)).then(function () { res(); }); }); }; var loops = Math.ceil(self.list.length / self.poolSize); for (var i = 0; i < loops; i++) { self.promise = self.promise.then(nextBatch); } self.promise = self.promise.then(function () { return self.finalCB(self._errors, self._responses); }).then(function () { return [self._errors, self._responses]; }); } function buildPoolQueue(self) { function _poolGenerator() { var item = self.list.shift(); if (!item) return; return createCBPromise(self, item).then(trackResponse.bind(self)).catch(errorCallBack(self, item)).then(_poolGenerator); } function createCBPromise(self, item) { return new Promise(function (resolve, reject) { if (self._stopped) return void resolve(null); return self.cb(item, resolve, reject, self); }); } self.promise = self.promise.then(function () { var pool = []; for (var i = 0; i < self.poolSize; i++) { pool.push(_poolGenerator()); } return Promise.all(pool); }).then(function () { return self.finalCB(self._errors, self._responses); }).then(function () { return [self._errors, self._responses]; }); } function createMasterPromise() { var promise, resolver = null; promise = new Promise(function (resolve) { resolver = resolve; }); return { resolver: resolver, promise: promise }; } var SeqPromiseClass = /*#__PURE__*/ function () { /** * SeqPromise the actual object is created here, allowing us to 'new' an object without calling 'new' * @param {Object} options - The options for setting the chain * @param {number} options.poolSize - The size of "simulated" thread pool * @param {boolean} options.autoStart - Will start the processing as soon as initialisation is complete * @param {*[]} options.list - The list of items to iterate over asynchronously * @param {boolean} options.useBatch - Switches from Stream mode, to batch * @param {number} options.batchSize - The size of each batch * @param {Object} options.context - Context to run functions in * @param {cb} options.cb - A function that returns a promise * @param {finalCB} options.finalCB - A function that that will be called once all done * @param {errorCB} options.errorCB - A function that returns a promise * * @callback cb * @param {*} Item - Item from the list * @param {*} Resolve - Resolve function for successful call * @param {*} Reject - Reject function for failed call * @param {*} Self - Item from the list * * @callback finalCB * @param {*[]} Errors - List of failed items * @param {*[]} Responses - List of completed items * * @callback errorCB * @param {*} Item - Item that failed * @param {*} Reason - Reason for failure, passed from reject */ function SeqPromiseClass(options) { _classCallCheck(this, SeqPromiseClass); if (typeOf(options.list) !== 'array') { throw new Error("Expecting list to be type Array, found type ".concat(typeOf(options.list))); } if (!(typeOf(options.cb) === 'function' || typeOf(options.cb) === 'asyncfunction')) { throw new Error("Expecting cb to be type Function, found type ".concat(typeOf(options.cb))); } this.list = options.list.slice(); this.cb = options.cb; this.finalCB = options.finalCB || blankFunction; this.errorCB = options.errorCB || blankFunction; this.useBatch = isTrue(options.useBatch); this.batchSize = options.batchSize || 1; this.poolSize = options.poolSize || 1; this.promise = null; this._globalPromiseResolver = null; this._stopped = false; this._errors = []; this._responses = []; if (options.context) { this.cb = options.cb.bind(options.context); this.finalCB = options.finalCB.bind(options.context); this.errorCB = options.errorCB.bind(options.context); } if (options.poolSize >= options.list.length) { this.poolSize = options.list.length; } var wrappedMasterPromise = createMasterPromise(); this.promise = wrappedMasterPromise.promise; this._globalPromiseResolver = wrappedMasterPromise.resolver; //Batch if (options.useBatch) { buildBatchQueue(this); } else { buildPoolQueue(this); } if (isTrue(options.autoStart)) { this._globalPromiseResolver(); return this; } } _createClass(SeqPromiseClass, [{ key: "start", value: function start() { this._globalPromiseResolver(); return this; } // noinspection JSUnusedGlobalSymbols }, { key: "stop", value: function stop() { this._stopped = !0; } }]); return SeqPromiseClass; }(); // 'new' an object var SeqPromise = function SeqPromise(options) { return new SeqPromiseClass(options); }; // trick borrowed from jQuery so we don't have to use the 'new' keyword SeqPromise.prototype = SeqPromiseClass.prototype; module.exports = SeqPromise;