# 1.3.10
- Modified: - `helpers/limit`: Adding parameter `assignControlFunction` to assing the controll function. - `helpers/index`: modified the export of the `limit` stuff. - Added: - `helpers/functionMethods*`: Added helpers for functions # 1.3.11 - Modified: - `helpers/functionMethods`: Adding `asnyc` detection # 1.3.12 - Modified: - `helpers/limit`: added the option `minDelay`. If provided, the calles are ensured to be delayed with this options. If `0` or smaller -> no delay is added.
This commit is contained in:
parent
d2be54d69a
commit
21688fb1fe
@ -4,8 +4,8 @@ cd "%DIR%"
|
|||||||
node contribute/toBrowser.js
|
node contribute/toBrowser.js
|
||||||
(npm publish --registry https://npm.zema.de/) && (
|
(npm publish --registry https://npm.zema.de/) && (
|
||||||
node contribute/toNodejs.js
|
node contribute/toNodejs.js
|
||||||
npm publish --registry https://npm.zema.de/
|
npm publish --registry https://npm.zema.de/ --tag latest
|
||||||
) || (
|
) || (
|
||||||
node contribute/toNodejs.js
|
node contribute/toNodejs.js
|
||||||
npm publish --registry https://npm.zema.de/
|
npm publish --registry https://npm.zema.de/ --tag latest
|
||||||
)
|
)
|
||||||
|
17
CHANGELOG.md
17
CHANGELOG.md
@ -216,4 +216,19 @@ Inital commit, which is working with the browser
|
|||||||
|
|
||||||
# 1.3.9
|
# 1.3.9
|
||||||
- Fixing:
|
- Fixing:
|
||||||
- `helpers/limit`: Now enrows all functions provided.
|
- `helpers/limit`: Now enrows all functions provided.
|
||||||
|
|
||||||
|
# 1.3.10
|
||||||
|
- Modified:
|
||||||
|
- `helpers/limit`: Adding parameter `assignControlFunction` to assing the controll function.
|
||||||
|
- `helpers/index`: modified the export of the `limit` stuff.
|
||||||
|
- Added:
|
||||||
|
- `helpers/functionMethods*`: Added helpers for functions
|
||||||
|
|
||||||
|
# 1.3.11
|
||||||
|
- Modified:
|
||||||
|
- `helpers/functionMethods`: Adding `asnyc` detection
|
||||||
|
|
||||||
|
# 1.3.12
|
||||||
|
- Modified:
|
||||||
|
- `helpers/limit`: added the option `minDelay`. If provided, the calles are ensured to be delayed with this options. If `0` or smaller -> no delay is added.
|
||||||
|
@ -1 +1 @@
|
|||||||
1.3.9
|
1.3.12
|
278
lib/helpers/functionMethods.spec.ts
Normal file
278
lib/helpers/functionMethods.spec.ts
Normal file
@ -0,0 +1,278 @@
|
|||||||
|
import { assert } from "chai";
|
||||||
|
import { describe, it } from "mocha";
|
||||||
|
import {
|
||||||
|
countAllArguments,
|
||||||
|
countArguments,
|
||||||
|
fillOptionalArguments,
|
||||||
|
_extractArgumentsPartFromFunction,
|
||||||
|
} from "./functionMethods";
|
||||||
|
|
||||||
|
describe("functionMethods", function () {
|
||||||
|
// Describe the required Test:
|
||||||
|
describe("countArguments - static functions", function () {
|
||||||
|
function test1() {
|
||||||
|
console.log("test");
|
||||||
|
}
|
||||||
|
|
||||||
|
function test2(a, b, c) {
|
||||||
|
console.log("test");
|
||||||
|
}
|
||||||
|
|
||||||
|
function test3(a, b, c = "hello") {
|
||||||
|
console.log("test");
|
||||||
|
}
|
||||||
|
|
||||||
|
function test4(a, b, c = "with, comma") {
|
||||||
|
console.log("test");
|
||||||
|
}
|
||||||
|
|
||||||
|
function test5(a, b, c = "with escaped ' quote and, comma") {
|
||||||
|
console.log("test");
|
||||||
|
}
|
||||||
|
|
||||||
|
function test6(
|
||||||
|
a,
|
||||||
|
b,
|
||||||
|
c = "with escaped ' quote and, comma",
|
||||||
|
d = 'and double " quotes, too!'
|
||||||
|
) {
|
||||||
|
console.log("test");
|
||||||
|
}
|
||||||
|
|
||||||
|
function test7(
|
||||||
|
a,
|
||||||
|
b,
|
||||||
|
c = "testFuncCalls".substr(1, 2),
|
||||||
|
d = "or maybe (string parenthesis)",
|
||||||
|
e = Math.sqrt(Math.pow(5, 2))
|
||||||
|
) {
|
||||||
|
console.log("test");
|
||||||
|
}
|
||||||
|
|
||||||
|
function test8(betterMakeSure, itWorksWith, longVariables = "too") {
|
||||||
|
console.log("test");
|
||||||
|
}
|
||||||
|
|
||||||
|
function test9(single) {
|
||||||
|
console.log("test");
|
||||||
|
}
|
||||||
|
|
||||||
|
function test10(single = "default") {
|
||||||
|
console.log("test");
|
||||||
|
}
|
||||||
|
|
||||||
|
function test11(a, b, c = { objects: true, test: "," }) {
|
||||||
|
console.log("test");
|
||||||
|
}
|
||||||
|
|
||||||
|
function test12(a, b, arrays = [true, 2, "three, "]) {
|
||||||
|
console.log("test");
|
||||||
|
}
|
||||||
|
|
||||||
|
function test13(a = "endingEmptyParenths".toString()) {
|
||||||
|
console.log("test");
|
||||||
|
}
|
||||||
|
|
||||||
|
function test14(singleInDouble = "'", b = 1) {
|
||||||
|
console.log("test");
|
||||||
|
}
|
||||||
|
|
||||||
|
function test15(doubleInSingle = '"', b = 1) {
|
||||||
|
console.log("test");
|
||||||
|
}
|
||||||
|
|
||||||
|
function test16(doubleInSingle = '"', b = 1, ...args) {
|
||||||
|
console.log("test");
|
||||||
|
}
|
||||||
|
|
||||||
|
function test17(p1: (p1, p2) => {}, p2) {
|
||||||
|
console.log("test");
|
||||||
|
}
|
||||||
|
|
||||||
|
function test18(p1: (p1, p2) => {}, p2 = null) {
|
||||||
|
console.log("test");
|
||||||
|
}
|
||||||
|
|
||||||
|
function test19(p1: (p1, p2) => number = (a, b) => 1, p2 = null) {}
|
||||||
|
|
||||||
|
const tests = [
|
||||||
|
[test1, 0, 0],
|
||||||
|
[test2, 3, 0],
|
||||||
|
[test3, 2, 1],
|
||||||
|
[test4, 2, 1],
|
||||||
|
[test5, 2, 1],
|
||||||
|
[test6, 2, 2],
|
||||||
|
[test7, 2, 2],
|
||||||
|
[test8, 2, 1],
|
||||||
|
[test9, 1, 0],
|
||||||
|
[test10, 0, 1],
|
||||||
|
[test11, 2, 1],
|
||||||
|
[test12, 2, 1],
|
||||||
|
[test13, 0, 1],
|
||||||
|
[test14, 0, 2],
|
||||||
|
[test15, 0, 2],
|
||||||
|
[test16, 0, 3],
|
||||||
|
[test17, 2, 0],
|
||||||
|
[test18, 1, 1],
|
||||||
|
[test19, 0, 2],
|
||||||
|
];
|
||||||
|
let idx = 1;
|
||||||
|
for (const [func, s, o] of tests) {
|
||||||
|
it(`correct counting of test${idx++}`, function () {
|
||||||
|
const res = countArguments(func);
|
||||||
|
assert.equal(
|
||||||
|
res.static,
|
||||||
|
s,
|
||||||
|
`Expecting static - parameters to ${s} to match ${res.static}`
|
||||||
|
);
|
||||||
|
assert.equal(
|
||||||
|
res.optional,
|
||||||
|
o,
|
||||||
|
`Expecting ${o} to match ${res.optional}`
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("countArguments - arrow functions", function () {
|
||||||
|
const tests = [
|
||||||
|
[/* 1*/ function empty() {}, 0],
|
||||||
|
[/* 2*/ function simple(a, b, c) {}, 3],
|
||||||
|
[/* 3*/ function withString(a, b, c = "hello") {}, 3],
|
||||||
|
[/* 4*/ function withStringAndComma(a, b, c = "with, comma") {}, 3],
|
||||||
|
[
|
||||||
|
/* 5*/ function withEscapedStuffInStringValue(
|
||||||
|
a,
|
||||||
|
b,
|
||||||
|
c = "with escaped ' quote and, comma"
|
||||||
|
) {},
|
||||||
|
3,
|
||||||
|
],
|
||||||
|
[
|
||||||
|
/* 6*/ function withEscapedStuffAndCommaInStringValue(
|
||||||
|
a,
|
||||||
|
b,
|
||||||
|
c = "with escaped ' quote and, comma",
|
||||||
|
d = 'and double " quotes, too!'
|
||||||
|
) {},
|
||||||
|
4,
|
||||||
|
],
|
||||||
|
[
|
||||||
|
/* 7*/ function withParenthesisInStringValues(
|
||||||
|
a,
|
||||||
|
b,
|
||||||
|
c = "testFuncCalls".slice(1, 2),
|
||||||
|
d = "or maybe (string parenthesis)",
|
||||||
|
e = Math.sqrt(Math.pow(5, 2))
|
||||||
|
) {},
|
||||||
|
4,
|
||||||
|
],
|
||||||
|
[
|
||||||
|
/* 8*/ function (betterMakeSure, itWorksWith, longVariables = "too") {},
|
||||||
|
3,
|
||||||
|
],
|
||||||
|
[/* 9*/ function (single) {}, 1],
|
||||||
|
[/*10*/ function (single = "default") {}, 1],
|
||||||
|
[/*11*/ function (a, b, c = { objects: true, test: "," }) {}, 3],
|
||||||
|
[/*12*/ function (a, b, arrays = [true, 2, "three, "]) {}, 3],
|
||||||
|
[/*13*/ function (a = "endingEmptyParenths".toString()) {}, 1],
|
||||||
|
[/*14*/ function (singleInDouble = "'", b = 1) {}, 2],
|
||||||
|
[/*15*/ function (doubleInSingle = '"', b = 1) {}, 2],
|
||||||
|
[/*16*/ () => {}, 0],
|
||||||
|
[/*17*/ (_ = 23) => {}, 1],
|
||||||
|
[/*18*/ (a) => {}, 1],
|
||||||
|
[/*19*/ (...a) => {}, 1],
|
||||||
|
[
|
||||||
|
/*20*/ (a) => {
|
||||||
|
return a;
|
||||||
|
},
|
||||||
|
1,
|
||||||
|
],
|
||||||
|
[/*21*/ (b = 1, a = {}) => {}, 2],
|
||||||
|
[/*22*/ (b = 1, a = [1, 2, 3]) => {}, 2],
|
||||||
|
// [/*23*/ function (foo, bar) {}.bind(null), 2], // 23 }'
|
||||||
|
[/*24*/ (a) => a, 1],
|
||||||
|
[/*25*/ (b, c, a = [1, 2, 4].map((v) => "x,y")) => `hello`, 3],
|
||||||
|
[/*26*/ (_ = 42) => console.log("test"), 1],
|
||||||
|
// [
|
||||||
|
// /*27*/ (a = `\(42\)`, b = `"blablabla,\",\"foo,\",\"bar"`) =>
|
||||||
|
// console.log("test"),
|
||||||
|
// 2,
|
||||||
|
// ],
|
||||||
|
[
|
||||||
|
/*28*/ (a = `([42, 43]\``, b = `"blablabla, foo(, \`\'bar:))"`) =>
|
||||||
|
console.log("test"),
|
||||||
|
2,
|
||||||
|
],
|
||||||
|
[
|
||||||
|
/*29*/ (
|
||||||
|
a = `\([42, 43]\)`,
|
||||||
|
b = function (a, b, c = "foo, also: bar") {
|
||||||
|
let x = [a, b];
|
||||||
|
return x;
|
||||||
|
}
|
||||||
|
) => console.log("test"),
|
||||||
|
2,
|
||||||
|
],
|
||||||
|
[
|
||||||
|
/*30*/ (
|
||||||
|
a = {
|
||||||
|
x: 'some" comma\'s, unclosed brackets, }},[, (, and escapes and whatnot"\\',
|
||||||
|
},
|
||||||
|
b
|
||||||
|
) => console.log("test"),
|
||||||
|
2,
|
||||||
|
],
|
||||||
|
];
|
||||||
|
for (const [func, all] of tests) {
|
||||||
|
it(`correct counting of: ${_extractArgumentsPartFromFunction(
|
||||||
|
func
|
||||||
|
)}`, function () {
|
||||||
|
const res = countArguments(func);
|
||||||
|
assert.equal(
|
||||||
|
res.total,
|
||||||
|
all,
|
||||||
|
`Expecting static - parameters to ${all} to match ${res.total}`
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("auto-fill", function () {
|
||||||
|
function test(p1, p2, p3 = null, p4 = null) {
|
||||||
|
return [p1, p2, p3, p4];
|
||||||
|
}
|
||||||
|
|
||||||
|
const tests: Array<[any[], any[], boolean, any[]]> = [
|
||||||
|
[[0, 1], [], false, [0, 1, undefined, undefined]],
|
||||||
|
[[0, 1], [2], false, [0, 1, 2, undefined]],
|
||||||
|
[[0, 1], [3], true, [0, 1, undefined, 3]],
|
||||||
|
[[0, 1], [2, 3], false, [0, 1, 2, 3]],
|
||||||
|
[[0, 1], [2, 3], true, [0, 1, 2, 3]],
|
||||||
|
];
|
||||||
|
|
||||||
|
for (const params of tests) {
|
||||||
|
it(`auto assigning parameters of: ${params.slice(0, 3)}`, function () {
|
||||||
|
const p = fillOptionalArguments(test, params[0], params[1], params[2]);
|
||||||
|
const r = params[3];
|
||||||
|
assert.deepEqual(
|
||||||
|
p,
|
||||||
|
r,
|
||||||
|
`Expecting static - parameters to ${p} to match ${r}`
|
||||||
|
);
|
||||||
|
|
||||||
|
const res = r.map((item) => {
|
||||||
|
if (item === undefined) {
|
||||||
|
return null;
|
||||||
|
} else return item;
|
||||||
|
});
|
||||||
|
|
||||||
|
assert.deepEqual(
|
||||||
|
(test as any)(...p),
|
||||||
|
res,
|
||||||
|
`Expecting the result to be equal ${p} to match ${res}`
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
132
lib/helpers/functionMethods.ts
Normal file
132
lib/helpers/functionMethods.ts
Normal file
@ -0,0 +1,132 @@
|
|||||||
|
const RE_EXTRACT_ARGS = /(^[a-z_](?=(=>|=>{)))|((^\([^)].+\)|\(\))(?=(=>|{)))/g;
|
||||||
|
const RE_VALUE_PARAMS = /(?<=[`"'])([^\`,].+?)(?=[`"'])/g;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Helper to extrat the code of the function:
|
||||||
|
*
|
||||||
|
* @example
|
||||||
|
* ```javascript
|
||||||
|
* function func(betterMakeSure, itWorksWith, longVariables = 'too') {}
|
||||||
|
*
|
||||||
|
* const r = extractArgumentsPartFromFunction(func);
|
||||||
|
*
|
||||||
|
* // => r = (betterMakeSure,itWorksWith,longVariables='too')
|
||||||
|
*
|
||||||
|
* ```
|
||||||
|
* @param func
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
export function _extractArgumentsPartFromFunction(func): string {
|
||||||
|
/**
|
||||||
|
* Based on the given sources:
|
||||||
|
*
|
||||||
|
* Source: https://stackoverflow.com/questions/42899083/get-function-parameter-length-including-default-params
|
||||||
|
* Source: https://stackblitz.com/edit/web-platform-jaxz82?file=script.js
|
||||||
|
*
|
||||||
|
* added the async related errors.
|
||||||
|
*/
|
||||||
|
|
||||||
|
let fnStr = func
|
||||||
|
.toString()
|
||||||
|
.replace("async", "")
|
||||||
|
.replace(RegExp(`\\s|function|${func.name}`, `g`), ``);
|
||||||
|
fnStr = (fnStr.match(RE_EXTRACT_ARGS) || [fnStr])[0].replace(
|
||||||
|
RE_VALUE_PARAMS,
|
||||||
|
``
|
||||||
|
);
|
||||||
|
return !fnStr.startsWith(`(`) ? `(${fnStr})` : fnStr;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Helper to count all arguments of an function (including the optional ones.)
|
||||||
|
* @param func The funtion to chekc
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
export function countAllArguments(func) {
|
||||||
|
/**
|
||||||
|
* Source: https://stackoverflow.com/questions/42899083/get-function-parameter-length-including-default-params
|
||||||
|
* Source: https://stackblitz.com/edit/web-platform-jaxz82?file=script.js
|
||||||
|
*/
|
||||||
|
|
||||||
|
const params = _extractArgumentsPartFromFunction(func);
|
||||||
|
|
||||||
|
if (params === "()") return 0;
|
||||||
|
|
||||||
|
let [commaCount, bracketCount, bOpen, bClose] = [
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
[...`([{`],
|
||||||
|
[...`)]}`],
|
||||||
|
];
|
||||||
|
[...params].forEach((chr) => {
|
||||||
|
bracketCount += bOpen.includes(chr) ? 1 : bClose.includes(chr) ? -1 : 0;
|
||||||
|
commaCount += chr === "," && bracketCount === 1 ? 1 : 0;
|
||||||
|
});
|
||||||
|
return commaCount + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Helper to count the arguments.
|
||||||
|
* @param func The function ot be analysed
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
export function countArguments(func): {
|
||||||
|
optional: number;
|
||||||
|
static: number;
|
||||||
|
total: number;
|
||||||
|
} {
|
||||||
|
const usedArguments = countAllArguments(func);
|
||||||
|
return {
|
||||||
|
optional: usedArguments - func.length,
|
||||||
|
static: func.length,
|
||||||
|
total: usedArguments,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Helper to fill provided arguments for the function.
|
||||||
|
* @param func The function ot be analysed
|
||||||
|
* @param providedArg The allready provided args
|
||||||
|
* @param argsToFill The Arguments to fill
|
||||||
|
* @param fromEnd A Flag to toggle, whether the arguments should be filled from the end or the beginning.
|
||||||
|
*/
|
||||||
|
export function fillOptionalArguments(
|
||||||
|
func,
|
||||||
|
providedArg: any[],
|
||||||
|
argsToFill: any[],
|
||||||
|
fromEnd = true
|
||||||
|
) {
|
||||||
|
const argumentOptions = countArguments(func);
|
||||||
|
|
||||||
|
if (argsToFill.length > argumentOptions.optional) {
|
||||||
|
// More arguments provided as possible => give a warning
|
||||||
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
argumentOptions.optional > 0 &&
|
||||||
|
argumentOptions.total > providedArg.length
|
||||||
|
) {
|
||||||
|
// Fill the arguments
|
||||||
|
const left = argumentOptions.total - providedArg.length;
|
||||||
|
for (let i = 0; i < left; i++) {
|
||||||
|
providedArg.push(undefined);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Now we cann fill some arguments.
|
||||||
|
const sourceOffset =
|
||||||
|
left >= argsToFill.length ? 0 : argsToFill.length - left;
|
||||||
|
for (let i = 0; i < argsToFill.length; i++) {
|
||||||
|
let idxToWrite = 0;
|
||||||
|
|
||||||
|
if (fromEnd) {
|
||||||
|
idxToWrite = argumentOptions.total - argsToFill.length + i;
|
||||||
|
} else {
|
||||||
|
idxToWrite = argumentOptions.total - left + i;
|
||||||
|
}
|
||||||
|
|
||||||
|
providedArg[idxToWrite] = argsToFill[sourceOffset + i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return providedArg;
|
||||||
|
}
|
@ -6,6 +6,7 @@
|
|||||||
import * as arrays from "./arrayMethods";
|
import * as arrays from "./arrayMethods";
|
||||||
import * as async from "./async";
|
import * as async from "./async";
|
||||||
import * as descriptors from "./descriptors";
|
import * as descriptors from "./descriptors";
|
||||||
|
import * as functions from "./functionMethods";
|
||||||
import * as subject from "./getSubject";
|
import * as subject from "./getSubject";
|
||||||
import * as ids from "./idMethods";
|
import * as ids from "./idMethods";
|
||||||
import * as json from "./jsonMethods";
|
import * as json from "./jsonMethods";
|
||||||
@ -22,6 +23,7 @@ import * as strings from "./stringMethods";
|
|||||||
export * from "./arrayMethods";
|
export * from "./arrayMethods";
|
||||||
export * from "./async";
|
export * from "./async";
|
||||||
export * from "./descriptors";
|
export * from "./descriptors";
|
||||||
|
export * from "./functionMethods";
|
||||||
export * from "./getSubject";
|
export * from "./getSubject";
|
||||||
export * from "./idMethods";
|
export * from "./idMethods";
|
||||||
export * from "./jsonMethods";
|
export * from "./jsonMethods";
|
||||||
@ -49,5 +51,6 @@ export {
|
|||||||
runtime,
|
runtime,
|
||||||
subject,
|
subject,
|
||||||
descriptors,
|
descriptors,
|
||||||
limit as lock,
|
functions,
|
||||||
|
limit,
|
||||||
};
|
};
|
||||||
|
@ -24,6 +24,34 @@ describe("limit", function () {
|
|||||||
throw Error("Failed to call sync");
|
throw Error("Failed to call sync");
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("single-call - with locking", async () => {
|
||||||
|
const sleepExtended = (delay, opts) => {
|
||||||
|
return new Promise((resolve) => {
|
||||||
|
opts.pauseTask();
|
||||||
|
setTimeout(() => {
|
||||||
|
opts.continueTask();
|
||||||
|
resolve(null);
|
||||||
|
}, delay);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const f = limitedCalls(sleepExtended, {
|
||||||
|
maxParallel: 0,
|
||||||
|
assignControlFunction(args, opts) {
|
||||||
|
args.push(opts);
|
||||||
|
return args;
|
||||||
|
},
|
||||||
|
});
|
||||||
|
const start = Date.now();
|
||||||
|
const promises = [f(100), f(100)];
|
||||||
|
await Promise.all(promises);
|
||||||
|
const end = Date.now();
|
||||||
|
if (end - start > 150) {
|
||||||
|
throw Error("Failed to call async");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
it("single-call - parallel", async () => {
|
it("single-call - parallel", async () => {
|
||||||
const f = limitedCalls(sleep, {
|
const f = limitedCalls(sleep, {
|
||||||
maxParallel: 2,
|
maxParallel: 2,
|
||||||
@ -62,5 +90,31 @@ describe("limit", function () {
|
|||||||
throw Error("Failed to call callbackBetween");
|
throw Error("Failed to call callbackBetween");
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
it("single-call - delay", async () => {
|
||||||
|
const f = limitedCalls<void>(async (...args) => {}, {
|
||||||
|
maxParallel: 0,
|
||||||
|
minDelay: 50,
|
||||||
|
});
|
||||||
|
const start = Date.now();
|
||||||
|
const promises = [f(100), f(100)];
|
||||||
|
await Promise.all(promises);
|
||||||
|
const end = Date.now();
|
||||||
|
if (end - start < 50) {
|
||||||
|
throw Error("Failed to call callbackBetween");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
it("single-call - delay (parallel)", async () => {
|
||||||
|
const f = limitedCalls<void>(async (...args) => {}, {
|
||||||
|
maxParallel: 10,
|
||||||
|
minDelay: 50,
|
||||||
|
});
|
||||||
|
const start = Date.now();
|
||||||
|
const promises = [f(100), f(100)];
|
||||||
|
await Promise.all(promises);
|
||||||
|
const end = Date.now();
|
||||||
|
if (end - start < 50) {
|
||||||
|
throw Error("Failed to call callbackBetween");
|
||||||
|
}
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -16,34 +16,42 @@ export type TLimitedOptions = {
|
|||||||
* The Id to use. If not provided, an specific id is generated
|
* The Id to use. If not provided, an specific id is generated
|
||||||
*/
|
*/
|
||||||
functionId: string;
|
functionId: string;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An queue that should be used. If not provided, a queue is used.
|
* An queue that should be used. If not provided, a queue is used.
|
||||||
*/
|
*/
|
||||||
queue: Array<[string, string, any[]]>;
|
queue: Array<[string, string, any[]]>;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Mapping for the Functions.
|
* Mapping for the Functions.
|
||||||
*/
|
*/
|
||||||
mapping: { [index: string]: (...args) => Promise<any> };
|
mapping: { [index: string]: (...args) => Promise<any> };
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An emitter to use.
|
* An emitter to use.
|
||||||
*/
|
*/
|
||||||
emitter: EventEmitter;
|
emitter: EventEmitter;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Helper function to request a lock.
|
* Helper function to request a lock.
|
||||||
*/
|
*/
|
||||||
getLock: (functionId: string, newTaskId: string) => boolean;
|
getLock: (functionId: string, newTaskId: string) => boolean;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An additional function, wich can be used between the next function in is called. e.g. sleep.
|
* An additional function, wich can be used between the next function in is called. e.g. sleep.
|
||||||
*/
|
*/
|
||||||
callbackBetween?: () => Promise<void>;
|
callbackBetween?: () => Promise<void>;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Number of elements, which could be called in parallel. 0 = sequntial
|
* Number of elements, which could be called in parallel. 0 = sequntial
|
||||||
*/
|
*/
|
||||||
maxParallel: number;
|
maxParallel: number;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A logger to use.
|
* A logger to use.
|
||||||
*/
|
*/
|
||||||
loggerLevel: false | LoggerLevel;
|
loggerLevel: false | LoggerLevel;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An overview with active Tasks. This is relevant for multiple Funtions.
|
* An overview with active Tasks. This is relevant for multiple Funtions.
|
||||||
*/
|
*/
|
||||||
@ -53,6 +61,21 @@ export type TLimitedOptions = {
|
|||||||
* An overview with active Tasks. This is relevant for multiple Funtions.
|
* An overview with active Tasks. This is relevant for multiple Funtions.
|
||||||
*/
|
*/
|
||||||
awaitingTasks: Set<string>;
|
awaitingTasks: Set<string>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Helper to assign the control function, for example on an async function.
|
||||||
|
*/
|
||||||
|
assignControlFunction: (
|
||||||
|
args: any[],
|
||||||
|
functions: {
|
||||||
|
pauseTask: () => void;
|
||||||
|
continueTask: () => void;
|
||||||
|
}
|
||||||
|
) => any[];
|
||||||
|
|
||||||
|
minDelay: number;
|
||||||
|
|
||||||
|
lastDone: number;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -112,10 +135,15 @@ export function limitedCalls<T>(
|
|||||||
settingsToUse.maxParallel < 0 || tasks.size <= settingsToUse.maxParallel
|
settingsToUse.maxParallel < 0 || tasks.size <= settingsToUse.maxParallel
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
assignControlFunction: (args, opts) => {
|
||||||
|
return args;
|
||||||
|
},
|
||||||
maxParallel: 0,
|
maxParallel: 0,
|
||||||
loggerLevel: false,
|
loggerLevel: false,
|
||||||
activeTasks: new Set(),
|
activeTasks: new Set(),
|
||||||
awaitingTasks: new Set(),
|
awaitingTasks: new Set(),
|
||||||
|
minDelay: -1,
|
||||||
|
lastDone: Date.now(),
|
||||||
};
|
};
|
||||||
|
|
||||||
const settingsToUse: TLimitedOptions = Object.assign(defaultSettins, options);
|
const settingsToUse: TLimitedOptions = Object.assign(defaultSettins, options);
|
||||||
@ -138,6 +166,7 @@ export function limitedCalls<T>(
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
settingsToUse.awaitingTasks.add(taskId);
|
settingsToUse.awaitingTasks.add(taskId);
|
||||||
|
settingsToUse.emitter.emit("execute");
|
||||||
};
|
};
|
||||||
|
|
||||||
const continueTask = () => {
|
const continueTask = () => {
|
||||||
@ -147,10 +176,14 @@ export function limitedCalls<T>(
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
settingsToUse.awaitingTasks.delete(taskId);
|
settingsToUse.awaitingTasks.delete(taskId);
|
||||||
|
settingsToUse.emitter.emit("execute");
|
||||||
};
|
};
|
||||||
|
|
||||||
// Add the functions.
|
// Add the functions.
|
||||||
args.push(pauseTask, continueTask);
|
args = settingsToUse.assignControlFunction(args, {
|
||||||
|
pauseTask,
|
||||||
|
continueTask,
|
||||||
|
});
|
||||||
|
|
||||||
// Push the Content to the emitter
|
// Push the Content to the emitter
|
||||||
settingsToUse.queue.push([functionId, taskId, args]);
|
settingsToUse.queue.push([functionId, taskId, args]);
|
||||||
@ -189,8 +222,10 @@ export function limitedCalls<T>(
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
settingsToUse.lastDone = Date.now();
|
||||||
|
|
||||||
// Emit, that there is a new task available
|
// Emit, that there is a new task available
|
||||||
settingsToUse.emitter.emit("data");
|
settingsToUse.emitter.emit("execute");
|
||||||
})
|
})
|
||||||
.catch((_) => {
|
.catch((_) => {
|
||||||
// Log some stuff
|
// Log some stuff
|
||||||
@ -200,8 +235,10 @@ export function limitedCalls<T>(
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
settingsToUse.lastDone = Date.now();
|
||||||
|
|
||||||
// Emit, that there is a new task available
|
// Emit, that there is a new task available
|
||||||
settingsToUse.emitter.emit("data", settingsToUse.functionId);
|
settingsToUse.emitter.emit("execute");
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
if (logger) {
|
if (logger) {
|
||||||
@ -210,8 +247,10 @@ export function limitedCalls<T>(
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
settingsToUse.lastDone = Date.now();
|
||||||
|
|
||||||
// Emit, that there is a new task available
|
// Emit, that there is a new task available
|
||||||
settingsToUse.emitter.emit("data", settingsToUse.functionId);
|
settingsToUse.emitter.emit("execute");
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -222,18 +261,34 @@ export function limitedCalls<T>(
|
|||||||
reject = _reject;
|
reject = _reject;
|
||||||
});
|
});
|
||||||
|
|
||||||
settingsToUse.emitter.emit("data");
|
settingsToUse.emitter.emit("execute");
|
||||||
|
|
||||||
return promise;
|
return promise;
|
||||||
};
|
};
|
||||||
|
|
||||||
if (settingsToUse.emitter.listeners("data").length == 0) {
|
if (settingsToUse.emitter.listeners("execute").length == 0) {
|
||||||
settingsToUse.emitter.on("data", () => {
|
const tryExecuteTask = () => {
|
||||||
if (settingsToUse.queue.length > 0) {
|
if (settingsToUse.queue.length > 0) {
|
||||||
// Get the Id and the Args.
|
// Get the Id and the Args.
|
||||||
const [functionId, taskId, args] = settingsToUse.queue[0];
|
const [functionId, taskId, args] = settingsToUse.queue[0];
|
||||||
|
|
||||||
if (settingsToUse.getLock(functionId, taskId)) {
|
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.
|
// Add the Task as active.
|
||||||
settingsToUse.activeTasks.add(taskId);
|
settingsToUse.activeTasks.add(taskId);
|
||||||
|
|
||||||
@ -270,7 +325,9 @@ export function limitedCalls<T>(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
};
|
||||||
|
|
||||||
|
settingsToUse.emitter.on("execute", tryExecuteTask);
|
||||||
}
|
}
|
||||||
|
|
||||||
return wrapped;
|
return wrapped;
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "nope",
|
"name": "nope",
|
||||||
"version": "1.3.9",
|
"version": "1.3.12",
|
||||||
"description": "NoPE Runtime for Nodejs. For Browser-Support please use nope-browser",
|
"description": "NoPE Runtime for Nodejs. For Browser-Support please use nope-browser",
|
||||||
"files": [
|
"files": [
|
||||||
"dist-nodejs/**/*",
|
"dist-nodejs/**/*",
|
||||||
|
Loading…
Reference in New Issue
Block a user