I would like to bring some experience and history to this conversation.
 First I've been writing node libraries and programs since 2009.  Many of
you know me and most of you probably don't because the node community has
grown exponentially over the past few years.

Shortly after node was made, the concept of the event emitter was added.
 We didn't have the current callback-last style, but had a convention that
non-blocking functions would return a special emitter known as a "Promise".
 Usage was something like this: <
https://github.com/creationix/node-router/blob/5d731874a210829ac389d7c978daa97f74a3c7e3/http_server.js#L176-L190
>

    var promise = node.fs.cat(filename, encoding);

    promise.addCallback(function (data) {
      body = data;
      headers = [ [ "Content-Type"   , content_type ],
                  [ "Content-Length" , body.length ]
                ];
      headers.push(["Cache-Control", "public"]);

      callback();
    });

    promise.addErrback(function () {
      notFound(req, res);
    });


As you can see it was quite verbose.  After almost a year of this we (the
smallish node community) decided to find a better control-flow pattern.  My
personal vote was for what I called shotgun continuables.  These were a
type of promise, but much easier to use than the event emitter based
promise in node (and *much* simpler than the A+/ES6 promise that's popular
today) <http://howtonode.org/do-it-fast>

In the end, Ryan decided to go for callback-last with error as the first
argument (current node style).  This was the most minimal interface and
supposedly the fastest.  Performance was a *big* deal because that was how
we were getting people excited about node to grow the community.  Node
still avoided functions that blocked on I/O of any kind, but did include
blocking versions of most the fs operations.  The pattern was to leave out
the callback, add "Sync" to the end of the name and return the value or
throw the error.  This is still the suggested style if you want to make a
blocking version of a function.

Shortly after this the concept of using streams wherever possible was baked
into node, sys.pump and later Stream.prototype.pipe were added to be able
to treat streams as first-class values.  This cleaned up a lot of code that
involved streams of data that needed layers of processing transforms
applied.

Node grew and grew, but the primitives for control-flow stayed the same.
 New people complained that callbacks were hard because they didn't
understand them.  Large companies started adopting node, etc.  Eventually
the A+ crowd started using node more and were evangelizing their view of
how control-flow should be done.  Bruno adopted node at his large company,
but decided to create streamline (a source to source transform) to handle
the complexity of callbacks in logic heavy code.  I later tried to push for
continuables again, but this time closer to the node style with a combined
(err, value) function.

Node was still growing at an amazing rate and the ratio of new people to
experienced people was getting pretty high.  I decided to port node.js to
lua <http://luvit.io> to experiment with using coroutines in this style of
non-blocking I/O.  It was an interesting experiment and I learned a lot of
techniques that are used today by libraries like gen-run, co, and galaxy.
 At the same time, node-fibers was created to give full coroutines to
node.js as a C++ addon.  It works really well and lets you do all the same
things I was doing in lua, but do them in node.  ES6 (the next version of
JavaScript) added generators and during the node 0.11 dev cycle (which
we're *still* in) they were implemented in V8 and added to node behind a
flag.  <http://howtonode.org/generators-vs-fibers>

Also thanks to the hard work of the A+ crowd, promises became part of ES6
and many browser APIs are moving that direction.

So today we have several years of experience using the node-style callbacks
with many sites like howtonode.org and callbackhell.com giving tips to be
more effective at writing your own control-flow logic.  We have multiple
competing promise libraries (all converging on A+) and the language itself
including promises soon.  We have full coroutines in node-fibers, light
more verbose coroutines in generators, source transforms like streamline,
etc.  This question of what is best is far from settled and there have
historically been quite the heated debate on this list over the years.

Personally I write most my code in node-style callbacks because it's
simple, I'm experienced in it, and I just want to get stuff done.  I don't
know where the future lies.  I expect to see more of promises since it's
gotten into the language itself.  I expect to see source transforms used
more and more as build tools mature and all browsers have sourcemaps.
 Generators are getting more widespread.  Stable Firefox has them today
without a flag.  node 0.11.x has it today with a flag, meaning you can use
it as long as you control the server (so app authors, not library authors).

-Tim Caswell
aka @creationix




On Sat, Apr 19, 2014 at 10:20 AM, // ravi <[email protected]> wrote:

> On Apr 19, 2014, at 12:28 AM, Kevin Burton <[email protected]>
> wrote:
>
> Thank you very much for all the replies. This has been most enlightening.
> Based on all of the replies and my situation I would like to explore
> Promises more. I think I understand the basic idea behind promises but lack
> specific implementation details. Does anyone have a good example of using Q
> and Node.js like the readme indicates:
>
> <snip happens>
>
> I get lost with denodify and deferred which the above simplification
> doesn't address. Any clarification for the newly initiated?
>
>
> Kevin,
>
> broadly, in the Q/promises world, it may help to think of functions as one
> of four types: (1) something that returns a value immediately, (2)
> something that is asynchronous and returns a value later to a callback
> using the node convention callback(err, result), (3) something that is
> asynchronous and returns a value later to a callback in a manner different
> from the node convention (or via an event, which I’ll ignore here), and
> finally, what we want: (4) something that returns a promise (whose return
> value can be obtained using its then() method).
>
> To structure your code using promises, you may want some or all such
> functions to return promises. Of course only the last one does, and so, Q
> provides a few mechanisms to coerce the others types to do so as well:
> denodify/ninvoke/nbind/etc are examples for type (2). Q.fcall() lets you
> wrap a return value as a promise. And Q.defer() lets you create and return
> your own promises for #4 or solve #3.
>
> Say you want to connect to a DB using a node-db module that uses standard
> Node conventions, and you want to insert a record, and finally write a log
> message using an async log routine that does not follow node conventions.
> You might do this (perhaps a bit contrived, to demonstrate the various
> Q-bits):
>
> var Q = require(‘q’);
> var db = require(‘db’);
> var logger = require(‘logger’);
>
> Q.ninvoke(db, “connect”, { host : ‘localhost’, user : ‘notroot’, password
> ; ‘super secret’ })
> .then
> (
> function(dbconn)
> {
> return( Q.ninvoke(dbconn, ‘insert’, { first : ‘Johnny’, last :
> ‘Appleseed’, device : ‘5s’ } ));
> }
> )
> .then(write_log)
> .fail(function(err) { console.error(err); })
> .done(process.exit);
>
> function write_log(result)
> {
> var deferred = Q.defer(); // we are going to wrap the async logger in a
> promise
> var logmsg = “DB :: INSERT :: new ID “ + result.id;
> logger.write
> (
> logmsg,
> function(rc)
> {
> if( rc === -1 ) // logging failed
> deferred.reject(new Error(“Write to DB log failed.”));
> else
> deferred.resolve();
> }
> );
> // we return a promise which will be fulfilled when logger.write() returns
> return(deferred.promise);
> }
>
>
> Note that all function references passed to .then() are to
> promise-returning functions. In write_log() you see an example of how you
> can create your own promise and return it, in this case, wrapping an async
> routine that uses a non-node style callback.
>
> In the above, we handle errors that can occur at any step in the chain at
> the end in the fail() method, but Q will let you catch them in the
> then() if you provide a second function reference:
> promise.then(success_func, fail_func). In either case, Q is smart enough
> to pass the first argument (err, in node style) to fail_func (or the
> function referenced in fail()) and the second argument (the result of the
> async operation in node style) to success_func.
>
> If you have more questions specifically about Q promises, you may also
> wish to join the Q mailing list (
> https://groups.google.com/forum/#!forum/q-continuum) and ask there.
>
> Regards,
>
> —ravi
>
>
>  --
> --
> Job Board: http://jobs.nodejs.org/
> Posting guidelines:
> https://github.com/joyent/node/wiki/Mailing-List-Posting-Guidelines
> You received this message because you are subscribed to the Google
> Groups "nodejs" group.
> To post to this group, send email to [email protected]
> To unsubscribe from this group, send email to
> [email protected]
> For more options, visit this group at
> http://groups.google.com/group/nodejs?hl=en?hl=en
>
> ---
> You received this message because you are subscribed to the Google Groups
> "nodejs" group.
> To unsubscribe from this group and stop receiving emails from it, send an
> email to [email protected].
> For more options, visit https://groups.google.com/d/optout.
>

-- 
-- 
Job Board: http://jobs.nodejs.org/
Posting guidelines: 
https://github.com/joyent/node/wiki/Mailing-List-Posting-Guidelines
You received this message because you are subscribed to the Google
Groups "nodejs" group.
To post to this group, send email to [email protected]
To unsubscribe from this group, send email to
[email protected]
For more options, visit this group at
http://groups.google.com/group/nodejs?hl=en?hl=en

--- 
You received this message because you are subscribed to the Google Groups 
"nodejs" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to [email protected].
For more options, visit https://groups.google.com/d/optout.

Reply via email to