1

I have a javascript app saving all data on server, then use REST API communicate server and client.

They works fine, until we start have more and more nested async call or nested sync call which hiding async call. For example:

function asyncFoo(callback) {
  callback();
}

function syncCallHidingAsyncCall(){
  syncStuff();
  asyncFoo(function(){
    syncFoo()
  });
}

function nestedAsyncCall(callback){
  asyncFoo(function(){
    anotherAsyncCall(callback);
  })
}
// this make refactor code become so hard.
// if we want add step2() after nestedAsyncCall();
// instead of add one line of code
// we need first add callback param in every asyncCall, then pass step2 as a callback

And some unnecessary async call:

// we actually only verify this once.
function isLogin(callback){
  if (!App._user) {
    ServerApi.getCurUser(function(data){
      App._user = data.user;
      callback(App._user)
    });
  }
  callback(App._user)
}

function syncCallNeedVerfifyLogin(callback){
  // user only need login once, so in most case this is really only a sync call.
  // but now I have to involve a unnecessary callback to get the return value
  isLogin(function(){
    callback(syncStuff())
  })
}

So after the project become bigger and bigger, we start forgot their relationship, which one need wait, which one will do magic. And more and more function become async only because some very small thing need be verify on server.

So I start feel their must be some design problem in this project. I am looking for the best practice or design patter, or some rules need follow in this kind heavy communicate app.

Thanks for help.

2
  • 1
    The pattern is called Promises Commented Apr 22, 2013 at 23:27
  • Thanks, I think thats what I am looking for, so in this pattern I will always return a defered object? I been looking at this before, but for some reason it wasn't make so much sense to me at that time. Commented Apr 23, 2013 at 0:02

3 Answers 3

4

They exist in several patterns to manage asynchronous data exchange and routine execution. They are called in different names as well:

  • Promises
  • EventEmitters
  • Deferred Objects/Deferreds
  • Control Flow Libraries
  • Futures
  • Callback aggregators
  • Observer / Publisher-Subscriber

A common implementation is jQuery's Deferred Objects which is also used in managing it's AJAX methods. In NodeJS, there is also AsyncJS and the native EventEmitter. There's even a 20-liner library made by some guy that implements EventEmitter which you could use.

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

Comments

0

As Bergi says in the comments, the pattern you're looking for is called deferred / promises. There's an implementation built into jQuery. From the docs:

a chainable utility object created by calling the jQuery.Deferred() method. It can register multiple callbacks into callback queues, invoke callback queues, and relay the success or failure state of any synchronous or asynchronous function.

There are a variety of other implementations some of which are outlined in this stackoverflow question.

3 Comments

If you're only looking for Deferreds/Promises, you should not choose jQuery's one :-)
It might not be the best implementation but it should solve his problem right? Also, if he's using it already it may make sense to utilise what's already available - though admittedly there doesn't look like there's a whole lot of jquery going on there ;)
I am using jquery + backbone + requirejs right now, I will try jQuery.Deferred first, seems this is good enough for me to start with.
0

Make yourself a queue system, something like:

function Queue() {
    this.queue = [];
}
Queue.prototype.i = -1;
Queue.prototype.add = function(fn) {
    if (typeof fn !== "function")
        throw new TypeError("Invalid argument");

    this.queue.push(fn);
}
Queue.prototype.next = function() {
    this.i++;
    if (this.i < this.queue.length) {
        this.queue[this.i].appy(this, arguments);
    }
}
Queue.prototype.start = function() {
    if (this.i !== -1)
        throw new Error("Already running")
    this.next.apply(this, arguments);
}

And use it like this:

var q = new Queue();

q.add(function() {
    // do something async
    // In the callback, call `this.next()`, passing
    //    any relevant arguments
})
q.add(function() {
    // do something async
    // In the callback, call `this.next()`, passing
    //    any relevant arguments
})
q.add(function() {
    // do something async
    // In the callback, call `this.next()`, passing
    //    any relevant arguments
})

q.start();

DEMO: http://jsfiddle.net/4n3kH/

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.