UNPKG

promise-mix

Version:

Node module with some useful Promise composition functionalities.

243 lines (226 loc) 9.27 kB
"use-strict"; const Operation = require('./operation'); /** * These utilities functions aren't quite on spot in this library as it should really execut operations on Promises not on the * downstream itself (which can be easily manipulated with a simple .then), but they might result useful sometimes/somehow to * shorten or make more readable a Promise chain. */ /** * This function checks the downstream against a check function and throws an error if the condition isn't met. * * @param {Function} check function to check the downstream. * @param {*} error the error to be thrown in case the check fails. * @returns {Promise} a Promise resolving if the check function returns true, rejecting otherwise. */ Promise.prototype._check = function (check, error = 'Unmet check condition.') { return this.then(res => { if (check(res)) { return res; } else { throw error; } }); }; /** * This function checks the downstream isn't undefined or throws an error. * * @param {*} error the error to be thrown in case the downstream is undefined/null. * @returns {Promise} a Promise resolving if the downstream is defined, rejecting otherwise. */ Promise.prototype._exists = function (error = 'Downstream is undefined.') { return this.then(res => { if (res) { return res; } else { throw error; } }); }; /** * This function revives the downstream of a rejected Promise passing downstream the error itself or a new value if specified. * * @param {*} newDownstream the new value to pass downstream to the revived Promise. * @returns {Promise} a Promise resolving the original caught error or the newDownstream value, if any. */ Promise.prototype._revive = function (newDownstream) { return this.catch(err => { let downstream = newDownstream ? newDownstream : err; if (!(newDownstream instanceof Promise)) downstream = Promise.resolve(downstream); return downstream; }); }; /** * This function combines the previous two checking and blocking the downstream on check failed, but immediately reviving it, * passing downstream the error or a new value if specified. * * @param {Function} check function to check the downstream. * @param {*} newDownstream the new value to pass downstream to the revived Promise. * @returns {Promise} a Promise resolving the original caught error or the newDownstream value, if any. */ Promise.prototype._checkOrRevive = function (check, newDownstream) { return this._check(check)._revive(newDownstream); }; const checkDataNotEmptyOrVoid = (data) => { const notNull = data !== null && data !== undefined && data !== ''; const notEmptyObject = (!(data instanceof Object) || Object.keys(data).length > 0); const notEmptyArray = (!(data instanceof Array) || data.length > 0); return notNull && notEmptyObject && notEmptyArray; } /** * Removes all undefined, null or empty items from any downstream object or array. * @returns {Promise} a Promise with a clean downstream. */ Promise.prototype._clean = function () { return this.then((datas) => { let cleanData; if (datas instanceof Array) cleanData = []; else if (datas instanceof Object) cleanData = {}; else return datas; for (let d in datas) { const data = datas[d]; if (checkDataNotEmptyOrVoid(data)) { if (cleanData instanceof Array) { cleanData.push(data); } else { cleanData[d] = data; } } } return cleanData; }); }; /** * This util hides a mux/deMux operation on a Promise downstream. * Basically splits the downstream into an array of Promises each handling a single item of the downstream, * uses each single downstream as input for a mapFunction which returns a new Promise to be replaced with * the original item one. * Finally it merges everything back with a deMux. * * @param {Function} mapFunction a function that accepts a single downstream item and returns a new Promise. * @returns {Promise} the deMux of the mapped Promises. */ Promise.prototype._map = function (mapFunction) { return this._mux(mux => mux.then(mapFunction).deMux()); }; /** * Stops the downstream for the given amount of milliseconds. * * @param {Number} milliseconds ms milliseconds to wait. * @returns {Promise} a Promise continuing the downstream after the given amount of milliseconds. */ Promise.prototype._sleep = function (milliseconds) { return this.then(donwstream => new Promise(res => setTimeout(() => res(donwstream), milliseconds))); }; /** * Really simple chain method which simply logs the downstream with a given tag. * This can be helpful to track the value of a downstream without having to write a whole .then(...) clause. * * @param {String} tag a tag to identify the log. * @returns {Promise} a Promise resolving the original downstream. */ Promise.prototype._log = function (tag) { return this.then(donwstream => { // eslint-disable-next-line no-console console.log(tag, donwstream); return donwstream; }); }; const loop = (prev, func, check, index = 0) => { return prev.then(dwn => func(dwn, index)).then(dwn => { if (check(dwn, index)) return dwn; else return loop(Promise.resolve(dwn), func, check, ++index); }); }; /** * Loops the execution of a function (may be returning Promises) until a given condition is met. * Then passes downstream the last result. * * @param {Function} iterationFunction the function to execute on the downstream at each iteration. * @param {Function} breakCheck the function that evaluates if the loop should break. * @returns {Promise} a Promise looping until the breakCheck returns true. */ Promise.prototype._loop = function (iterationFunction, breakCheck) { return loop(this, iterationFunction, breakCheck); }; /** * Excecutes a given function on the downstream only if a certain condition is met. * * @param {Function} check a function to evaluate wether to execute the function or not. * @param {*} operationToExecute an operation to be executed on the downstream if the check returns true. * @returns {Promise} a promise resolving the downstream or the result of the function if the check returns true. */ Promise.prototype._when = function (check, operationToExecute) { const operation = new Operation(operationToExecute); return this.then(downstream => { if (check(downstream)) return operation.get(downstream); return Promise.resolve(downstream); }); }; /** * Excecutes a given function on the downstream only if a certain condition is met. * * @param {Function} check a function to evaluate wether to execute the function or not. * @param {*} ifOperation an operation to be executed on the downstream if the check returns true. * @param {*} elseOperation an operation to be executed on the downstream if the check returns false. * @returns {Promise} a promise resolving the result of the if function or the result of the else function if the check returns true or false. */ Promise.prototype._ifElse = function (check, ifOperation, elseOperation) { const ifOp = new Operation(ifOperation); const elseOP = new Operation(elseOperation); return this.then(downstream => { if (check(downstream)) return ifOp.get(downstream); return elseOP.get(downstream); }); }; /** * Keeps only the properties with given labels / indexes of the downstream. * * @param {Array} keys the array of labels / indexes of properties to keep from the downstream * @returns {Promise} a promise resolving with the "picked" downstream. */ Promise.prototype._pick = function (keys) { if (!(keys instanceof Array)) keys = [keys]; return this.then(downstream => { let output; if (downstream instanceof Array) { output = []; } else if (downstream instanceof Object) { output = {}; } else { throw `Cannot read properties ${keys} of ${downstream}`; } for (let k in keys) { const key = keys[k]; if (output instanceof Array) { output.push(downstream[key]); } else { output[key] = downstream[key]; } } return Promise.resolve(output); }); }; /** * Keeps only the properties with given labels / indexes of the downstream. * * @param {String} key the label to keep in the downstream. * @returns {Promise} a promise resolving with the value of the downstream at the given key. */ Promise.prototype._just = function (key) { return this.then(downstream => { if (!(downstream instanceof Array) && !(downstream instanceof Object)) { throw `Cannot read properties ${key} of ${downstream}`; } return Promise.resolve(downstream[key]); }); }; /** * Ignores any downstream and resolves with empty value. * * @returns {Promise} a promise resolving with empty downstream. */ Promise.prototype._ignore = function () { return this.then(() => { return Promise.resolve(); }); };