A callback hell free(ish) way to do REST...

141 views
Skip to first unread message

Amir Yasin

unread,
Jan 13, 2015, 12:57:33 AM1/13/15
to nod...@googlegroups.com
I've written a couple of large Express apps and a few smaller ones and invariably anything complex starts descending into callback hell.  I'm working on a solution for that and I think I'm ready at this point for some community feedback.  Bear in mind that there's a fair bit of work left to do and it's not a 'production' project yet.  Nevertheless there's enough there to give you a sense of where I'm going with it...feedback and constructive criticism would be greatly appreciated.

The project is located at https://github.com/ayasin/frhttp

Thanks

Tom Boutell

unread,
Jan 14, 2015, 8:59:50 AM1/14/15
to nod...@googlegroups.com
I appreciate the problem you're trying to solve, but your solution feels like a reinvention of the async module, or perhaps a promises module like "q". Yours will be fine until you need "if / while / each" logic and you have to add more and more reimplementations of features already found in those.

I'm not convinced you need to comingle the problem of routing HTTP requests with the problem of callback hell in a single library.

Amir Yasin

unread,
Jan 14, 2015, 2:45:45 PM1/14/15
to nod...@googlegroups.com
First, thanks for the comment.  It's not really a reinvention of the async module in that there's no "order of execution" specified by the author.  Things get executed as data becomes available.  Think of it more as "I need these signals to be ready and then I can transform them into this other set of signals".  You end up in a situation where you're defining functions that do very specific tasks and produce output.  They can be called multiple times if necessary within the context of producing an output or just once.  The other part of this is that if any of them throws an exception or has an error you get a nice output to the console "Function suchAndSuch threw an exception (the exception) on inputs {a json object of the inputs}".  Much nicer to deal with than the errors you get from callbacks.  I'm not sure I understand what you mean by "if/when/each" logic.  You can handle all of those in this as far as I can tell between producing values which get consumed by something else (each) and filtering via "enter" (if/when).

Amir

Tom Boutell

unread,
Jan 15, 2015, 10:23:04 AM1/15/15
to nod...@googlegroups.com

On Wednesday, January 14, 2015 at 2:45:45 PM UTC-5, Amir Yasin wrote:
First, thanks for the comment.  It's not really a reinvention of the async module in that there's no "order of execution" specified by the author.  Things get executed as data becomes available.  Think of it more as "I need these signals to be ready and then I can transform them into this other set of signals".  You end up in a situation where you're defining functions that do very specific tasks and produce output.  They can be called multiple times if necessary within the context of producing an output or just once.  The other part of this is that if any of them throws an exception or has an error you get a nice output to the console "Function suchAndSuch threw an exception (the exception) on inputs {a json object of the inputs}".  Much nicer to deal with than the errors you get from callbacks.  I'm not sure I understand what you mean by "if/when/each" logic.  You can handle all of those in this as far as I can tell between producing values which get consumed by something else (each) and filtering via "enter" (if/when).

Amir

Hmm. I've  started reading about Functional Reactive Programming. As near as I can tell your library resembles baconjs, but with bindings specific to dealing with express requests. But after reading the documentation and your comments I am still pretty lost as to how it decides what to invoke when, and how you would handle a situation where you must iterate asynchronously over many objects returned from a query, for instance.

Amir Yasin

unread,
Jan 15, 2015, 8:25:31 PM1/15/15
to nod...@googlegroups.com
FRHTTP heavily uses Bacon, but it's not itself an extension of Bacon.  To determine what to call, in the execution phase (when the native node HTTP server hands it a connection) it builds out a series of combined streams (bacon combineTemplate is heavily used for this) from a series of dynamic observables.  Where did these dynamic observables come from?  That's determined by looking at the params/produces of all the when blocks in your route as well as your render block.  There's a lot of stuff going on here that's hard to detail in a short post, but if you're interested you can read the code for routeExecutor.js (the entry point is the routeExecutor function).  

As to how you would do a db query, as luck would have it I wrote a sample for that today :).  Here's the sample:  https://github.com/ayasin/frhttp/blob/master/samples/db.simulation.js (that one deals with async values being produced by something outside of your control like a DB), here's a sample that deals with just accumulating values and returning a single result (also using enter to break once it's done): https://github.com/ayasin/frhttp/blob/master/samples/factorial.js

My plan is definitely to improve the docs, write more samples and probably some blog posts about all this.  The structure of the doc could definitely be better, was there a specific section that confused you?

Also the error handling front, here's what an uncaught exception in a route in FRHTTP outputs to the console (you can see the code that threw this at https://github.com/ayasin/frhttp/blob/master/samples/uncaught.exception.js):

ERROR: function (Crashes On Purpose) threw an exception (TypeError: Object #<Object> has no method 'crashNow') on input: {
  "internal:__ready": 1,
  "demo": "a value"
}

Notice that it tells you who threw it, what the exception was, and most importantly what the input to the function was that caused it to be thrown.  If you're doing functional programming and only using your inputs it should be very easy to track down the cause.

Amir 

Alexey Petrushin

unread,
Jan 17, 2015, 3:50:54 AM1/17/15
to nod...@googlegroups.com
There's also another approach, it's better showcased by example.

Here's the Demo - 10 min blog http://example.monojs.org

Try to find callbacks there ;)

Tom Boutell

unread,
Jan 19, 2015, 1:04:21 PM1/19/15
to nod...@googlegroups.com
This is interesting... I think I understand it a bit better now. It
seems not wildly unlike async.auto:

https://github.com/caolan/async#autotasks-callback

Which lets you specify dependencies between tasks rather than an order of tasks.

However, you've added the idea of streams to that picture.
> --
> Job board: http://jobs.nodejs.org/
> New group rules:
> https://gist.github.com/othiym23/9886289#file-moderation-policy-md
> Old group rules:
> https://github.com/joyent/node/wiki/Mailing-List-Posting-Guidelines
> ---
> You received this message because you are subscribed to a topic in the
> Google Groups "nodejs" group.
> To unsubscribe from this topic, visit
> https://groups.google.com/d/topic/nodejs/TF6-k4lWKGE/unsubscribe.
> To unsubscribe from this group and all its topics, send an email to
> nodejs+un...@googlegroups.com.
> To post to this group, send email to nod...@googlegroups.com.
> To view this discussion on the web visit
> https://groups.google.com/d/msgid/nodejs/04c68ca3-3211-4f10-9b81-cbee5a5f8b17%40googlegroups.com.
>
> For more options, visit https://groups.google.com/d/optout.



--


THOMAS BOUTELL, DEV & OPS
P'UNK AVENUE | (215) 755-1330 | punkave.com
Reply all
Reply to author
Forward
0 new messages