3

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?

6
  • 2
    This question might be over my head, but could you set some kind of global boolean variable in A.Foo() to true and have B.Bar() immediately return if the global variable is false? Commented Aug 28, 2012 at 16:47
  • 2
    @Travesty3, that would work - but it would make the code coupled to not only both functions, but a random global variable also. Commented Aug 28, 2012 at 16:49
  • The answer to this depends on your code structure. The most general would be to pass in a function to call in B.Bar(). Otherwise it depends on what you're actually doing, you could also use some sort of deferred-style mechanism so you don't need to pass it in. Commented Aug 28, 2012 at 16:52
  • Do you mind about putting some "well-known" object, like observer (you mentioned it in your original post) that both A.Foo and B.Bar will know about? Also, do you mind about using jQuery or you would like to have a completely 3rd-party libraries independent solution? Commented Aug 28, 2012 at 16:57
  • something similar to function chaining? Commented Aug 28, 2012 at 17:01

2 Answers 2

2

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.

Sign up to request clarification or add additional context in comments.

Comments

0

Dojo allows you to listen to function calls.

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

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.