case
ADM Blog
13Mar/120

Make a JavaScript getter act both as getter and as a function

So I have a object that exposes a getter to retrieve data after some expensive synchronous computation.

 
    function processData() {
       // lots of stuff happening here
       return result;
    }
 
   return {
      get data () {
          // some checks happening here
           return procesData();
      }
   }
var obj = new MyObject();
var x = obj.data; // this stops things for 2 or 3 seconds
// .. carry on

Since this is part of an already used library, is getting pretty hard to change the public interface for backwards compatibility reasons.
The thing is I've added a asynchronous processData method and i want to make that available as well without changing the public methods. The normal thing to do would be to convert the getter into a method called getData() and getDataAsync(callback) but that wouldn't work because it will annoy some people when they see their code isn't working anymore.

The solution couldn't be easier but it took me a while to figure it out and as always, it's sharing time.

 
    function processData() {
       // lots of stuff happening here
       return result;
    }
 
    function processDataAsync(callback) {
      if (!arguments.length || arguments.length && typeof arguments[0] !== "function") {
            return processData();
      }
      // lots of stuff happening here but asynchronously 
      callback(result);
    }
 
    processDataAsync.__proto__ = {valueOf : processData};
 
   return {
      get data () {
           // some checks happening here
           return processDataAsync;
      }
   }
var obj = new MyObject();
var x = obj.data; // get data synchronously
// .. carry on after 2 seconds
 
var obj = new MyObject();
var x = obj.data(); // still a synchronous call since no callback was given
// .. carry on after 2 seconds
 
var obj = new MyObject();
obj.data(function(result) {
    // get the data here after few seconds without blocking anything
}
// .. carry on right away

So now the same getter can be used from now on as a asynchronous function if used with parentheses and given a callback or as a simple getter.