UNPKG

nope-js-node

Version:

NoPE Runtime for Nodejs. For Browser-Support please use nope-browser

196 lines (195 loc) 8.3 kB
"use strict"; /** * @author Martin Karkowski * @email m.karkowski@zema.de */ Object.defineProperty(exports, "__esModule", { value: true }); exports.limitedCalls = exports.getLimitedOptions = void 0; const events_1 = require("events"); const index_browser_1 = require("../index.browser"); const idMethods_1 = require("./idMethods"); const setMethods_1 = require("./setMethods"); /** * Helper to get the default options in a shared context * @param options The options to enhance the defaults. * @returns The options. */ function getLimitedOptions(options) { const defaultSettings = { queue: [], mapping: {}, emitter: new events_1.EventEmitter(), maxParallel: 0, loggerLevel: false, activeTasks: new Set(), awaitingTasks: new Set(), }; return Object.assign(defaultSettings, options); } exports.getLimitedOptions = getLimitedOptions; /** * Function to limit the calls based on the settings. * @param func The function to use. This should be an async function. * @param options The Options. * @returns */ function limitedCalls(func, options) { let logger = false; // Define the Default-Settings const defaultSettins = { functionId: Date.now().toString(), queue: [], mapping: {}, emitter: new events_1.EventEmitter(), getLock: () => { const tasks = (0, setMethods_1.difference)(settingsToUse.activeTasks, settingsToUse.awaitingTasks); if (logger) { logger[settingsToUse.loggerLevel](`active Tasks: [${Array.from(tasks)}]; awaiting Tasks: [${Array.from(settingsToUse.awaitingTasks)}];`); } return (settingsToUse.maxParallel < 0 || tasks.size <= settingsToUse.maxParallel); }, assignControlFunction: (args, opts) => { return args; }, maxParallel: 0, loggerLevel: false, activeTasks: new Set(), awaitingTasks: new Set(), minDelay: -1, lastDone: Date.now(), }; const settingsToUse = Object.assign(defaultSettins, options); const functionId = settingsToUse.functionId; settingsToUse.mapping[functionId] = func; if (settingsToUse.loggerLevel) { logger = (0, index_browser_1.getNopeLogger)("limited-calls", settingsToUse.loggerLevel); } const wrapped = function (...args) { // Generate the Call-ID const taskId = (0, idMethods_1.generateId)(); const pauseTask = () => { if (logger) { logger[settingsToUse.loggerLevel](`pausing taskId="${taskId}".`); } settingsToUse.awaitingTasks.add(taskId); settingsToUse.emitter.emit("execute"); }; const continueTask = () => { if (logger) { logger[settingsToUse.loggerLevel](`continuing taskId="${taskId}".`); } settingsToUse.awaitingTasks.delete(taskId); settingsToUse.emitter.emit("execute"); }; // Add the functions. args = settingsToUse.assignControlFunction(args, { pauseTask, continueTask, }); // Push the Content to the emitter settingsToUse.queue.push([functionId, taskId, args]); // lets have an item, that contains the resolve let resolve = null; let reject = null; // Define a callback, which is called. const cb = (error, result) => { settingsToUse.emitter.off(taskId, cb); if (error) { reject(error); } else { resolve(result); } // Delete the Task settingsToUse.activeTasks.delete(taskId); settingsToUse.awaitingTasks.delete(taskId); if (typeof settingsToUse.callbackBetween === "function") { if (logger) { logger[settingsToUse.loggerLevel](`using 'callbackBetween' for taskId="${taskId}". Calling now`); } settingsToUse .callbackBetween() .then((_) => { if (logger) { logger[settingsToUse.loggerLevel](`awaited 'callbackBetween' for taskId="${taskId}". Transmitting results now`); } settingsToUse.lastDone = Date.now(); // Emit, that there is a new task available settingsToUse.emitter.emit("execute"); }) .catch((_) => { // Log some stuff if (logger) { logger[settingsToUse.loggerLevel](`something went wrong with 'callbackBetween' for taskId="${taskId}". Transmitting results now!`); } settingsToUse.lastDone = Date.now(); // Emit, that there is a new task available settingsToUse.emitter.emit("execute"); }); } else { if (logger) { logger[settingsToUse.loggerLevel](`no 'callbackBetween' for taskId="${taskId}". Transmitting results now`); } settingsToUse.lastDone = Date.now(); // Emit, that there is a new task available settingsToUse.emitter.emit("execute"); } }; settingsToUse.emitter.on(taskId, cb); const promise = new Promise((_resolve, _reject) => { resolve = _resolve; reject = _reject; }); settingsToUse.emitter.emit("execute"); return promise; }; if (settingsToUse.emitter.listeners("execute").length == 0) { const tryExecuteTask = () => { if (settingsToUse.queue.length > 0) { // Get the Id and the Args. const [functionId, taskId, args] = settingsToUse.queue[0]; if (settingsToUse.getLock(functionId, taskId)) { const diff = Date.now() - settingsToUse.lastDone; if (settingsToUse.minDelay > 0 && diff < settingsToUse.minDelay) { // Recall our routine setTimeout(tryExecuteTask, settingsToUse.minDelay - diff + 10, null); return; } if (settingsToUse.maxParallel > 0) { settingsToUse.lastDone = Date.now(); } // Add the Task as active. settingsToUse.activeTasks.add(taskId); // Remove the items: settingsToUse.queue.splice(0, 1); // Try to perform the call. try { if (logger) { logger[settingsToUse.loggerLevel](`calling function '${functionId}' for the task taskId="${taskId}"`); } settingsToUse.mapping[functionId](...args) .then((result) => { if (logger) { logger[settingsToUse.loggerLevel](`called function '${functionId}' for the task taskId="${taskId}"`); } settingsToUse.emitter.emit(taskId, null, result); }) .catch((error) => { if (logger) { logger[settingsToUse.loggerLevel](`called function '${functionId}' for the task taskId="${taskId}", but resulted in an error`); } settingsToUse.emitter.emit(taskId, error, null); }); } catch (error) { settingsToUse.emitter.emit(taskId, error, null); } } } }; settingsToUse.emitter.on("execute", tryExecuteTask); } return wrapped; } exports.limitedCalls = limitedCalls;