Neues Node.js-Buch
Alle Artikel

Create a generic function to create functions that can only be called once

Problem

You want to create a generic function that converts a function to a function that can only be called once.

Ingredients

  • a closure
  • the apply method

Directions

  1. Given: a function that executes only once (based on yesterday’s recipe).

    function printSomething() {
      let called = false;
      if(called) {
        return undefined;
      } else {
        called = true;
        console.log('something');
        printSomething = function() {};
      }
    }
    printSomething();        // (1) Ok
    printSomething();        // (2) Does nothing
  2. Create a function once() that accepts a function as parameter and returns another function.

    function once(fn) {
      return function() {
        ...
      }
    }
  3. Add a variable to memorize if the function has been called.

    function once(fn) {
      let called = false;
      return function() {
        ...
      }
    }
  4. Inside the returned function check that variable. If it is true, then return undefined

    function once(fn) {
      let called = false;
      return function() {
        if(called) {
          return undefined;
        }
        ...
      }
    }
  5. … otherwise set the variable to true and call the passed function.

    function once(fn) {
      let called = false;
      return function() {
        if(called) {
          return undefined;
        } else {
          called = true;
          fn.apply(this, arguments);
        }
      }
    }
  6. Optimization: use void 0 instead of return undefined, which are less characters.

    function once(fn) {
      let called = false;
      return function() {
        if(called) {
          void 0;
        } else {
          called = true;
          fn.apply(this, arguments);
        }
      }
    }
  7. Optimization: replace if and else with the ternary operator.

    function once(fn) {
      let called = false;
      return function() {
        return called ? void 0 : ((called = true), fn.apply(this, arguments));
      }
    }
  8. Optimization: use a fat arrow function for once().

    const once = (fn) => {
      let called = false;
      return function() {
        return called ? void 0 : ((called = true), fn.apply(this, arguments));
      }
    }
  9. Now remove all the boilerplate logic from printSomething() and use once() to create another function printSomethingOnce().

    function printSomething() {
      console.log('something');
    }
    let printSomethingOnce = once(printSomething);
  10. Voilá, now if printSomethingOnce() is called a second time, it simply does nothing.
```javascript
function printSomething() {
  console.log('something');
}
let printSomethingOnce = once(printSomething);
printSomethingOnce();        // (1) Ok
printSomethingOnce();        // (2) Does nothing
```

Alternative recipes