Tim, your historical perspective is great. I got involved later than you, 
in 2010, a couple months before Ryan decided to drop promises and go with 
callbacks. So in our project, we did start with promises, then we went back 
to callbacks for a few months, and then to streamline.

I noticed one small error in your chronology: fibers appeared only a few 
days after streamline. They were both released in January 2011.

Also, streamline was not the first transpiler. narrative.js and 
stratified.js were there before. I got my syntax inspiration from narrative 
but I went with a different algorithmic approach.

And as you say, the debate is still not settled today. It's a bit of a 
jungle.

Bruno

On Saturday, April 19, 2014 7:02:12 PM UTC+2, Tim Caswell wrote:
>
> 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] 
> <javascript:>>wrote:
>
>> On Apr 19, 2014, at 12:28 AM, Kevin Burton 
>> <[email protected]<javascript:>> 
>> 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]<javascript:>
>> To unsubscribe from this group, send email to
>> [email protected] <javascript:>
>> 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] <javascript:>.
>> 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