You could chain pipes in the following style:
var p = Q.pipe( ["users", "stream"], function(params, subjects) { // users and streams are here // use params.users, params.streams, // subjects.user, subjects.stream Users.AppUser.SELECT('*').where({...}) .execute( this.fill('appUsers', true) ); } ).set( ["users", "appUsers"], function(params, subjects) { // now we have users and appUsers filled finalResult(args1, arg2); } ); Broadcast.User.SELECT('*').where({...}) .execute(p.fill('users')); Broadcast.Stream.SELECT('*').where({...}) .execute(p.fill('streams')); function finalResult() { ... }
Here is what is happening above. The pipe is created, and calls to the database are sent, with the callbacks generated by the pipe. Once both "users" and "streams" have arrived, the first function is called. It does something with the received objects, and then issues another query to the database, with a callback generated by the same pipe. Notice the second parameter: this.fill(..., true). This indicates to the pipe that the currently executing function in the pipe should no longer be called from now on. Otherwise, when the "appUsers" would be filled, the pipe would again start from the top and execute the function, resulting in an infinite loop.
Once the "appUsers" are filled, the second function can run, which can do something with both users and appUsers. It decides to simply call the third function right away (not asynchronously). This is mostly for illustration purposes, because we could have just made postToWall a regular function.