1
\$\begingroup\$

Yes, I know there are plenty of libraries to get this done (Q, step, async), but only for learning I'm wondering if the following code is ok and usable or have major drawbacks and it is only usable at a small programs.

Usage:

var g = new Manager();

g.then ( 
    function() { 
        console.log("hello");
        setTimeout( function () { g.done("hello done") }, 2000 );
    }
);

g.start();

g.then ( 
    function() { 
        console.log("two");
        g.done ("two done");
    }
);

g.then ( 
    function(resp) { 
        console.log("I got: " + resp);
        setTimeout( function () { g.done("happy ending") }, 2000 );
    }
);

g.then ( 
    function(resp) { 
        console.log("last msg: " + resp);
        g.done();
    }
);

Class:

//...............................
// class Manager
//...............................
function Manager () { 
    this.functionList = [];
    this.allDone = false;
} // ()

//...............................
//...............................
Manager.prototype.then = function (fun) { 
    this.functionList.push (fun);
    if (this.allDone) { 
        this.allDone = false;
        this.done(this.previousResult);
        console.log ( "then(): continuing");
    } 

} // ()

//...............................
//...............................
Manager.prototype.done = function (prevRes) { 
    this.previousResult = prevRes;
    if ( this.where <= this.functionList.length-2 ) { 
        this.where++;
        this.functionList [this.where](this.previousResult);
    } else { 
        this.allDone = true;
        console.log ( "done(): stopping because allDone: where=" + this.where + " and length = " + this.functionList.length);
    } 

} // ()

//...............................
//...............................
Manager.prototype.start = function () { 
    this.allDone = false;
    this.where=-1;
    this.done();
} // ()
\$\endgroup\$
0

1 Answer 1

1
\$\begingroup\$

Some points:

  • g.then(function() { … g.done ("two done");}) I (personally) don't like that signature, I'm more used to callbacks that get passed as parameters (g.then(function(done) { … done ("two done");})). They tend make the chained functions better reusable - in your case it might not matter since you're using dedicated function expressions anyway which are bound to g. And your signature simplifies argument handling in the Manager :-)
  • this.allDone = false; seems to be a misnomer for me. The way you're using it, it should rather be called startedAndNotRunning. Technically if you're initialising the empty list they are all done already :-)
  • This is highly confusing:

    if ( this.where <= this.functionList.length-2 ) { 
        this.where++;
        …
    

    What happens to the last two functions? Nothing special. It better is written

    this.where++;
    if (this.where < this.functionList.length) {
        …
    

    Then the iterator variable (where) will become the length of the array after the "loop", which is the common behaviour. Yet, you cannot use done any more to [re]start the chain - you'd need to decrement where before. Therefore I'd recommend to restructure the methods to

    function Manager () {
        …
        this.where = 0;
    }
    Manager.prototype.start = function () {
        // you might reset allDone and where
        this.next();
    };
    Manager.prototype.done = function (prevRes) { 
        this.previousResult = prevRes;
        this.where++;
        this.next();
    };
    Manager.prototype.next = function() {
        if (this.where < this.functionList.length)
            this.functionList[this.where](this.previousResult);
        else { 
            this.allDone = true;
            console.log ( "done(): stopping at where=" + this.where + " with length = " + this.functionList.length);
        }
    };
    Manager.prototype.then = function (fun) { 
        this.functionList.push (fun);
        if (this.allDone) {
            this.allDone = false;
            this.start();
            console.log ( "then(): continuing");
        } 
    };
    
\$\endgroup\$
4
  • \$\begingroup\$ I appreciate your comments. The internal implementation Manager may be not the best, but I was more interested in the usability of the interface (client code) and with regard to the implementation (here, consider your version) if you think that it can be used as a library for general development, or it must be restricted to cases with few ".then" \$\endgroup\$ Commented Jul 8, 2013 at 15:13
  • \$\begingroup\$ What do you mean by "cases with few then"? It has no performance problems if you mean that. On the usability of the interface, only my first bullet point is interesting. It often is subjective, so ask your actual client :-) \$\endgroup\$
    – Bergi
    Commented Jul 8, 2013 at 15:57
  • \$\begingroup\$ Yes, its on performance. And here I suppose there is no need to think in "thread safe" implementations. \$\endgroup\$ Commented Jul 8, 2013 at 16:33
  • 1
    \$\begingroup\$ Yes. And since the Manager is dedicated for asynchronous function queues, you don't need to worry about the call stack as well. \$\endgroup\$
    – Bergi
    Commented Jul 8, 2013 at 17:30

Your Answer

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

Not the answer you're looking for? Browse other questions tagged or ask your own question.