Problem
You want to create a generic function that converts a function to a function that can be called at most n times.
Ingredients
- a closure
- the
apply
method
Directions
-
Given: a function that executes only once (based on recipe 14) and another function (
printSomething()
) that should be converted.const once = (fn) => { let called = false; return function() { return called ? void 0 : ((called = true), fn.apply(this, arguments)); } } function printSomething() { console.log('something'); } const printSomethingMaxFive = max(printSomething, 5); printSomethingMaxFive(); // Should print "something" printSomethingMaxFive(); // Should print "something" printSomethingMaxFive(); // Should print "something" printSomethingMaxFive(); // Should print "something" printSomethingMaxFive(); // Should print "something" printSomethingMaxFive(); // Should do nothing
-
Add a parameter (
max
) that contain the maximum number of times the function should be called (and rename the function tomax()
).const max = (fn, max) => { let called = true; return function() { return called ? void 0 : ((called = true), fn.apply(this, arguments)); } }
-
Change the
called
variable to contain a number.const max = (fn, max) => { let called = 0; return function() { return called ? void 0 : ((called = true), fn.apply(this, arguments)); } }
-
Inside the closure check if
called
is greater thanmax
.const max = (fn, max) => { let called = 0; return function() { return called >= max ? void 0 : ((called = true), fn.apply(this, arguments)); } }
-
Inside the closure (i.e., when the function is called) increase the
called
variable.const max = (fn, max) => { let called = 0; return function() { return called >= max ? void 0 : ((called++), fn.apply(this, arguments)); } }
-
Voilá, now if
printSomethingMaxFive()
is called more than 5 times, it simply does nothing.const max = (fn, max) => { let called = 0; return function() { return called >= max ? void 0 : ((called++), fn.apply(this, arguments)); } } function printSomething() { console.log('something'); } const printSomethingMaxFive = max(printSomething, 5); printSomethingMaxFive(); // Should print "something" printSomethingMaxFive(); // Should print "something" printSomethingMaxFive(); // Should print "something" printSomethingMaxFive(); // Should print "something" printSomethingMaxFive(); // Should print "something" printSomethingMaxFive(); // Should do nothing
Variants
-
If you don’t need the context when the function is executed, you can also (1) use arrow functions, (2) call the given function directly, (3) use rest parameters and (4) omit the
return
keyword from the closure.const max = (fn, max) => { let called = 0; return (...args) => { called >= max ? void 0 : ((called++), fn(...args)); } }
Alternative recipes
- Use a self-defining function.