JavaScript Once Function

By  on  

Every so often you have a function which you only want to run once.  Oftentimes these functions are in the form of event listeners which may be difficult to manage.  Of course if they were easy to manage, you'd just remove the listeners but that's a perfect world and sometimes you simply want the ability to only allow a function to be called once.  Here's the JavaScript function to make that possible!

The JavaScript

Think of this once function as a wrapper for the function you provide:

function once(fn, context) { 
	var result;

	return function() { 
		if(fn) {
			result = fn.apply(context || this, arguments);
			fn = null;
		}

		return result;
	};
}

// Usage
var canOnlyFireOnce = once(function() {
	console.log('Fired!');
});

canOnlyFireOnce(); // "Fired!"
canOnlyFireOnce(); // nada

The wrapping function is fired only once because a tracker variable is used to ensure the function is only executed once.  Many JavaScript toolkits offer this as a feature but the code to accomplish this feat is so small that it's good to have available in the case that you can dodge a JavaScript toolkit!

Recent Features

  • By
    Responsive and Infinitely Scalable JS Animations

    Back in late 2012 it was not easy to find open source projects using requestAnimationFrame() - this is the hook that allows Javascript code to synchronize with a web browser's native paint loop. Animations using this method can run at 60 fps and deliver fantastic...

  • By
    Being a Dev Dad

    I get asked loads of questions every day but I'm always surprised that they're rarely questions about code or even tech -- many of the questions I get are more about non-dev stuff like what my office is like, what software I use, and oftentimes...

Incredible Demos

  • By
    Introducing LazyLoad 2.0

    While improvements in browsers means more cool APIs for us to play with, it also means we need to maintain existing code.  With Firefox 4's release came news that my MooTools LazyLoad plugin was not intercepting image loading -- the images were loading regardless of...

  • By
    Simple Image Lazy Load and Fade

    One of the quickest and easiest website performance optimizations is decreasing image loading.  That means a variety of things, including minifying images with tools like ImageOptim and TinyPNG, using data URIs and sprites, and lazy loading images.  It's a bit jarring when you're lazy loading images and they just...

Discussion

  1. phiggins

    go on, follow up with debounce and throttle.

  2. Tim Oxley

    Available on npm as https://github.com/isaacs/once

  3. hjess

    I usually do this, more directly, instead:

    function toBeRunOnce(args)  {
      // some stuff to do
      console.log('toBeRunOnce has completed');
      toBeRunOnce = function() {};
    }
    
    
    toBeRunOnce(blahBlah);  // "... has completed"
    toBeRunOnce(haHa);       // nothing
    

    Your solution is better for cases where you have a function that you might want run once in some contexts, but run multiple times in some other context. But do you ever really encounter that?

    • teoman

      His solution is a utility function which can be adapted for all upcoming methods without forgetting to assign owner function to a null value.

      Also, yes there are many scenarios where a method can be used once in some cases and more in some others. Assume you have a product with trial and pro. You can only send single message for trial users, but many for pro users. So you can easily use same function for both purposes. Obviously you will need to validate trial vs pro member at server side, but at least at client, it is fair enough.

      Also there are many usage scenarios for games as well.

  4. Thanks for this!

  5. Allain

    Second source bit can be rewritten as the below for brevity:

    function once(fn, context) { 
      var result;
       
      return function() { 
        if (fn) {
          result = fn.apply(context || this, arguments);		   
          fn = null;	
        }
          
        return result;
      };
    }
    
    • Great point, I’ve updated my post! Thank you!

    • Özgür

      Perfect, thanks

  6. I’m learning js 8 months already, but i’m still struggling with call/apply. Everytime when i’m reading docs, it feels like someone is fucking my brain.

  7. Alex Marinenko

    You can go even further and do as following:

    Function.prototype.once = function (ctx) {
      var fn = this;
      var aArgs = Array.prototype.slice.call(arguments, 1);
        
      return function () {
        var result;
        if (fn) {
          result = fn.apply(ctx || this, aArgs);
          fn = null;
        }
        return result;
      };
    }
    
  8. Thanks for this. I’m not very good at JavaScript, so sorry if my question is dumb.

    Where’s arguments defined?

    And is context defined to allow the function to be applied on an object as well as being capable of calling it globally?

  9. Guest

    Can someone explain why you might want to have a function like this. I was asked this question on an interview and didn’t know what it was or how you might use it. Also, does this pattern have a name?

    • For anything you don’t want to be able to be executed more than once? Mostly for initialization stuff.

  10. Alin Stefan

    Very nice and elegant approach. I use this function in my projects and it’s working well.
    Thanks!

  11. Ted Hopp

    Nice solution. I’d be inclined to change

    fn = null;

    to

    fn = context = null;

    There’s no reason for the returned function to hang on to a reference to <pre class=”javascript”>context</pre> once <pre class=”javascript”>fn</pre> has been called.

Wrap your code in <pre class="{language}"></pre> tags, link to a GitHub gist, JSFiddle fiddle, or CodePen pen to embed!