Neues Node.js-Buch
Alle Artikel

Create a generic function to create functions that can be called at most n times

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

  1. 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
  2. Add a parameter (max) that contain the maximum number of times the function should be called (and rename the function to max()).

    const max = (fn, max) => {
      let called = true;
      return function() {
        return called ? void 0 : ((called = true), fn.apply(this, arguments));
      }
    }
  3. 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));
      }
    }
  4. Inside the closure check if called is greater than max.

    const max = (fn, max) => {
      let called = 0;
      return function() {
        return called >= max ? void 0 : ((called = true), fn.apply(this, arguments));
      }
    }
  5. 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));
      }
    }
  6. 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

  1. 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