Xeoncross
Xeoncross

Reputation: 57254

What is the correct way to implement decoupled code / callbacks in Javascript?

I have several different JavaScript objects with methods that i want to keep separate. However, I need some type of observer, callback, or plugin design so that I can trigger these methods at the correct time.

For example, A.Foo() should almost always run after B.Bar() - but I do not want to place a call to A.Foo() inside B.Bar() because there are those odd times it should not run after B.Bar(). That would be coupling my code and I know that's bad.

I want to approach this problem as if B.Bar() is just doing it's job and does not ever plan on knowing about A.Foo() or any other function that might want to tag along with it. In other words, plugin support for future developers.

How do you design modular, non-coupled code with observer or event based callbacks in Javascript?

Upvotes: 3

Views: 1160

Answers (2)

Kernel James
Kernel James

Reputation: 4074

Dojo allows you to listen to function calls.

dojo.connect("foo", A, function(){
  B.bar();
});

Upvotes: 0

ForbesLindesay
ForbesLindesay

Reputation: 10712

It depends whether your scenario is really event like in nature, or more of an asynchronous operation:

Event Based

Using something like microevent you could do the following. This pattern is really common and simple, so I suggest that at some point you have a go at implimenting this yourself, as it's great from an understanding point of view.

MicroEvent.mixin(A);

A.Foo = function () {
  //Do stuff A needs to do, then:
  A.trigger('foo-complete');
};

//Somewhere nice and separate and decoupled
A.bind('foo-complete', B.Bar);

If you need to do more complex things like filter the events, map the events etc. Reactive Extensions (which are based of the C# libraries) are really powerful, but that's a big library to load in if you don't need the functionality.

Async operation

Using Callbacks

This can be done using callbacks, which is great for fairly simple operations:

A.Foo = function (cb) {
  //Do stuff A needs to do, then:
  if (cb) cb();
};

A.Foo(B.Bar);

Using promises

These initially look more complicated, but they are easilly composable, and have nice ways of handling errors built in.

Using Q which is one of the more popular promise libraries (again, there are loads of these):

A.Foo = function () {
  var def = Q.defer();
  setTimeout(function () {
    //Simulate async operation
    def.resolve();
  }, 1000);
  return def.promise;
};

A.Foo().then(B.Bar).end();

Using control flow libraries

Control flow libraries (arguably promises are a special case of this) aim to help you compose operations that are based on the callback system.

Upvotes: 2

Related Questions