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.
