Hone logo
Hone
Problems

Implement a JavaScript once Function

Many programming tasks involve functions that should only be executed a single time. For example, initializing a connection, setting up a configuration, or logging a message that should only appear once. This challenge asks you to create a higher-order function in JavaScript that ensures another function is called at most once.

Problem Description

Your task is to implement a JavaScript function called once. This function will take another function (let's call it func) as an argument. The once function should return a new function. When this returned function is called, it should execute func only if func has not been executed before. Subsequent calls to the returned function should do nothing and return the result of the first execution.

Key requirements:

  • The once function must accept a single argument: the function to be wrapped.
  • The once function must return a new function.
  • The returned function should execute the original function on its first invocation.
  • All subsequent invocations of the returned function should return the cached result of the first execution, without re-executing the original function.
  • The returned function should accept any arguments and pass them to the original function on its first call.
  • The returned function should return the value returned by the original function on its first call.

Expected behavior: If the returned function is called multiple times, the original function is executed only on the very first call. Subsequent calls return the same result as the first call.

Edge cases:

  • What if the original function returns undefined?
  • What if the original function throws an error? (For this challenge, assume the original function will not throw an error, or if it does, the behavior of once is not strictly defined beyond the first execution).

Examples

Example 1:

function greet(name) {
  console.log(`Hello, ${name}!`);
  return `Greeting sent to ${name}`;
}

const greetOnce = once(greet);

const result1 = greetOnce('Alice'); // Output: Hello, Alice!
console.log(result1); // Output: Greeting sent to Alice

const result2 = greetOnce('Bob');   // No console output from greet
console.log(result2); // Output: Greeting sent to Alice (same as result1)

const result3 = greetOnce('Charlie'); // No console output from greet
console.log(result3); // Output: Greeting sent to Alice (same as result1)

Output:

Hello, Alice!
Greeting sent to Alice
Greeting sent to Alice
Greeting sent to Alice

Explanation: The greet function is called only once with 'Alice'. Subsequent calls to greetOnce with 'Bob' and 'Charlie' do not re-execute greet, but return the cached result from the first call.

Example 2:

let counter = 0;
function incrementCounter() {
  counter++;
  return counter;
}

const incrementOnce = once(incrementCounter);

console.log(incrementOnce()); // Output: 1
console.log(incrementOnce()); // Output: 1
console.log(incrementOnce()); // Output: 1
console.log(counter);       // Output: 1

Output:

1
1
1
1

Explanation: incrementCounter is called only on the first invocation of incrementOnce. The counter variable is only incremented once.

Example 3:

function getConfiguration() {
  console.log("Fetching configuration...");
  return { setting: "value" };
}

const getConfigOnce = once(getConfiguration);

const config1 = getConfigOnce(); // Output: Fetching configuration...
console.log(config1); // Output: { setting: 'value' }

const config2 = getConfigOnce(); // No console output
console.log(config2); // Output: { setting: 'value' }

Output:

Fetching configuration...
{ setting: 'value' }
{ setting: 'value' }

Explanation: The expensive operation "Fetching configuration..." is performed only once. Subsequent calls retrieve the cached configuration object.

Constraints

  • The once function must be implemented in plain JavaScript, without using external libraries.
  • The implementation should be efficient, ensuring minimal overhead on subsequent calls.
  • The original function (func) can accept any number of arguments.

Notes

Consider how you can maintain state (whether the function has been called and what its result was) across multiple invocations of the returned function. Closures are likely to be a key concept here.

Loading editor...
javascript