Johan
Johan

Reputation: 35194

Callbacks as object properties

I would like to get a similar functionality to this:

function foo(before, after){

    before();

    setTimeout(function(){
         after();
    },100)
}

foo(function(){
    console.log('before');
}, function(){
    console.log('after');
});

When returning an object instead of using callbacks (psuedo code):

var foo = function() {

     before();         

     setTimeout(function(){
         after();
     },100)

     return {
         before: before,
         after: after
     }
};

foo().before(function(){
    console.log('before');
});

foo().after(function(){
    console.log('after');
});

Or perhaps even

foo().before(function(){
    console.log('before');
}).after(function(){
    console.log('after');
});

Is this possible?

Upvotes: 2

Views: 106

Answers (5)

James Porter
James Porter

Reputation: 1923

Phrogz' answer is good but FWIW you can also do this without prototypes:

function foo() {
  ret = function () { return ret.call(); };
  ret.call = function () {
    this._before();
    setTimeout(this._after,100);
  };
  ret.before = function (x) { this._before = x; return this; };
  ret.after = function (x) { this._after = x; return this; };
  return ret;
};

test = foo().before(function(){
  console.log('before');
}).after(function(){
  console.log('after');
});

Calling:

test();

does what you would expect.

Upvotes: 1

Dan Tao
Dan Tao

Reputation: 128317

It's almost possible.

The problem with your proposed usage example, though, is that it involves foo() being called before before or after callbacks are specified. Meanwhile, in your example implementation, these callbacks are executed within foo. So that isn't achievable: you can't retroactively change what happened in a method after that method has already run.

If what you really want is for the callbacks supplied via foo to be executed nearly right away—i.e, if pure synchronous execution isn't mandatory—you can get what you're after with a simple setTimeout call:

function foo() {
  var callbacks = {
    before: function() {},
    after: function() {}
  };

  setTimeout(function() {
    callbacks.before();
    setTimeout(callbacks.after, 100);
  }, 0);

  return {
    before: function(callback) {
      callbacks.before = callback;
      return this;
    },

    after: function(callback) {
      callbacks.after = callback;
    }
  };
}

Here, foo() will return an object that exposes methods to modify the callbacks object closed over within the method body of foo. These methods will then be used on the next tick of the event loop, after foo() has executed.

I've created an example at JSBin if you're interested in seeing this code in action.

Upvotes: 2

Guffa
Guffa

Reputation: 700302

Yes, your function can return an object with before and after methods that keep track of which callbacks has been set, and when both are set it can call them:

function foo() {

    var beforeFunc = null, afterFunc = null;

    function activate() {
        beforeFunc();
        window.setTimeout(afterFunc, 100);
    }

    var obj = {
        before: function(f) {
            beforeFunc = f;
            if (afterFunc != null) activate();
            return obj;
        },
        after: function(f) {
            afterFunc = f;
            if (beforeFunc != null) activate();
            return obj;
        }
    };

    return obj;

}

// As both callbacks need to be set, the order is not important
foo().after(function(){
    console.log('After');
}).before(function(){
    console.log('Before');
});

Demo: http://jsfiddle.net/4HypB/

Upvotes: 4

Douglas
Douglas

Reputation: 37763

Here's another variation:

var createFooBuilder = function () {
    var before = function() {};
    var after = function() {};

    var func = function () {
        before();
        setTimeout(function () {
            after();
        }, 100)
    };

    func.before = function(value) {
        before = value;
        return func;
    }
    func.after = function(value) {
        after = value;
        return func;
    }

    return func;
};

var foo = createFooBuilder();

foo.before(function () {
    console.log('before');
}).after(function () {
    console.log('after');
});

foo();

I'd certainly recommend trying to make jQuery Deferreds work for you, then you can have lists of after functions.

Upvotes: 1

Phrogz
Phrogz

Reputation: 303215

function foo(){};
foo.prototype.before = function(f){ this._before = f };
foo.prototype.after  = function(f){ this._after  = f };
foo.prototype.go = function(){
  this._before();
  setTimeout(this._after,100);
}

var f = new foo;
f.before(function(){ … });
f.after(function(){ … });
f.go();

But then again, you might as well make this simpler:

function foo(){};
foo.prototype.go = function(){
  this.before();
  setTimeout(this.after,100);
}

var f = new foo;
f.before = function(){ … };
f.after  = function(){ … };
f.go();

Upvotes: 1

Related Questions