7 ms·
Broader adoption of JS on the server will only begin after ES6 generators land in Node. Due to the complexity of async code, writing JS on the server is not eas
by jeswin 13y ago
Broader adoption of JS on the server will only begin after ES6 generators land in Node. Due to the complexity of async code, writing JS on the server is not easy for the average team.
As of now, you could use Promises but it is still kind of messy without generators. Here is a simple example:
#Without generators
Credentials.get({ token })
.then (credentials) =>
User.get({ username: credentials.username })
#With generators
credentials = yield Credentials.get({ token })
User.get({ username: credentials.username })
We use FaceBook's regenerator project to compile ES6 features down to Node's currently supported Javascript features. If you don't want to use the --harmony flag, you should really try it out. https://github.com/facebook/regenerator https://github.com/facebook/regenerator
- phaer 13y agoWhat are the advantages of compiling to ES5 instead of using --harmory?
- arianvanp 13y ago"Cross-platformness". Someone who wants to use your library and doesnt use harmony won't have to start his node process up with `--harmony` which is sort of a win I guess
- ihaveqvestion 13y agoIn the submitted article, two callback methods are usually provided (one for success and another for failure). Is that possible when using generators? If so, what does that look like?
- sciolistse 13y agoIn generator code you'd use try/catch to handle failure instead. try { var user = yield Users.get(1234); return user.name; } catch(ex) { // do whatever } rather than.. return Users.get(1234).then( function (u) { return u.name; }, function (ex) { /* do whatever */ } );
- spion 13y agoThose are not entirely equivalent. For example, if you make a typo when writing "user" or "u", the first one will catch that typo (cannot get property name of undefined) while the second one will not. Users.get(1234) .then(u => u.name) .catch(ex => 1/* do whatever */);
- camus2 13y agoGood but can you yield from the global scope? if not you still need to write at least a function. The hardest stuff in JS is that : function F(){throw "error"} try{setTimeout(F,1000)}catch(error){ console.log("error")} we cant catch async errors outside the async callback , will generators fix this ? ( by the way , that's why you should never throw any error yourself in js ).
- coderzach 13y agoThat's why node modules generally don't throw exceptions, but rather pass them as the first argument to the callback. I think you can catch async errors if you call the throw method on the generator. http://wiki.ecmascript.org/doku.php?id=harmony:generators#methodthrow http://wiki.ecmascript.org/doku.php?id=harmony:generators#me...
- domenicd 13y agoYou cannot yield from the global scope; you have to explicitly be inside a generator function. This isn't really a problem in practice except for one-off-scripts, since most modules don't have side effects but instead export functions (possibly generator functions). But for those one-off scripts it is indeed a bit awkward... you could imagine ways to get around it. You can catch async errors with generators and promises; see e.g. the linked post, or http://jlongster.com/A-Study-on-Solving-Callbacks-with-JavaScript-Generators http://jlongster.com/A-Study-on-Solving-Callbacks-with-JavaS...
- masklinn 13y ago> we cant catch async errors outside the async callback , will generators fix this ? You can inject an exception into a generator via the throw method[0], and it'll be raised at the yield, as if `yield` had thrown[1]. JS exception handling facilities are still crap, though. [0] http://wiki.ecmascript.org/doku.php?id=harmony:generators#methodthrow http://wiki.ecmascript.org/doku.php?id=harmony:generators#me... [1] http://wiki.ecmascript.org/doku.php?id=harmony:generators#internal_methodthrow http://wiki.ecmascript.org/doku.php?id=harmony:generators#in...
- esailija 13y agoCool trick with the redundant indentation which can be applied to generators too: #Without generators Credentials.get({ token }) .then(credentials => User.get({ username: credentials.username })) #With generators var credentials = yield Credentials.get({ token }) User.get({ username: credentials.username })
- omgtehlion 13y agoThis might be not a very good example. But generators have more good features than just cleaner code. 1. You write code almost like synchronous one. You can use any control-flow statements you are familiar with. Just dont forget to turn a promise into real value with `yield` keyword. 2. How much times have you wrote such statements? X.prototype.foo = function() { var self = this; // or var that = this; this.bar().then(function(x) { that.baz(x); }); }; or }.bind(this)); With yield you can just write your logic, not a boilerplate: X.prototype.foo = async(function*() { var x = yield this.bar(); this.baz(); });
- esailija 13y ago1. Yes this is the problem. It is very easy to write something like this: var a = yield A(); var b = yield B(); This is massively less performant than: var a = A(); var b = B(); a = yield a; b = yield b; In promises you naturally do `Promise.all([A(), B()]).spread((a, b) => //Use a and b);` 2. Why did you stop using arrow functions? X.prototype.foo = function() { this.bar().then(x => this.baz(x)); }; 3. You are overvaluing control-flow constructs. Try-catch is always a catch-all error silencer where as promises can extend `.catch()` and `.finally()` to overcome flaws. Hand written loops are not that common, one mostly uses .filter, .map and .reduce in these kind of work flows.
- omgtehlion 13y ago1. You can easily write the same crappy code with promises anyway. And if you do think that `Promise.all` will save you, then you can use it with yield. With destructuring you even don’t need `.spread`. Even more, with yield you can easily achieve parallel execution just moving `yield` keyword to the place where you need actual value. Good luck doing this with promises. 2. Unfortunately, traceur-compiler does not support fat arrow yet; 3. If you use try-catch just as a silencer, man I have bad news for you. And if you provide real error-handling in `.fail()` then what’s the problem writing the same code inside catch clause?
- omgtehlion 13y agoCompletely agree! This isn't a new thihg anyway, there are currently a lot of implementations, I even wrote one myself[1]. By the way, most (sane) libraries that use yield/await perfom a lot better, than vanilla promises. Because when you do .then() on already resolved promise you have to wait for the next tick, or setImmediate or whatewer. Yield-based implementations can continue right away in this case. [1]: https://github.com/omgtehlion/asjs https://github.com/omgtehlion/asjs
- esailija 13y agoThat is not true at all about Promises/A+. The requirement is to always calls handlers with an empty stack and in a certain order. In practice only naivest implementations would call platform scheduler for every `.then()`. Implementations can in all cases call the handlers synchronously when it would matter.
- quarterto 13y ago> In practice, this requirement ensures that onFulfilled and onRejected execute asynchronously, after the event loop turn in which then is called, and with a fresh stack. http://promises-aplus.github.io/promises-spec/#notes http://promises-aplus.github.io/promises-spec/#notes (emphasis mine)
- esailija 13y agoThat's just the specification. Doing exactly what the spec says is the definition of a naive implementation.
- delian66 13y agoWhat is the point of having a specification, if everyone decides not to follow it ?
- esailija 13y agoI did not say that one should not to comply to a specification. There is a big difference between implementations complying to specification and implementations where the source code is basically just duplication of the spec (which obviously runs very slowly). So my point is that even if the spec might say that "Do A, then B and then C", that's not what an implementation has to do EXACTLY as long as the outcome is exactly the same in both cases. For example a property load is a hugely complex operation in the ECMAScript spec that would take thousands of instructions to implement naively. In practice e.g. V8 will in many cases get away literally with one machine instruction in best case. None of the specified property load steps are obviously happening but yet somehow V8 is spec compliant.
- zsombor 13y agoWhilst thanks to lack of choice JavaScript is so important on the browser side, I would hope the Node infatuation would eventually pass where you do have a choice. Async calls in Node are done via explicit passing of closures. Callbacks, Promises etc all look bad compared to simple sequential instructions like: credentials := NewCredentials(token) User.findByName(credentials.userName) The above is in Go, and it is just as fast if not more. Whilst all requests are on their own goroutines, the runtime will schedule them using the same efficient async io that does not need to be made visible as in JavaScript.
- spion 13y agovar withThis = (object, f) => function bound() { return f.apply(object, arguments); } function getCredentialsByToken(token) { return newCredentials(token) .then(withThis(User, User.findByName)); }
- empthought 13y agoThat reinforces zsombor's point nicely.
- spion 13y agoI was hoping it shows that it really depends on your style (procedural or functional). Thanks to generators and arrow functions, ES6 lets you write code using either. When you're writing in functional style, you'd use arrows, which work nicely both for small lambdas and for writing combinators that can be partially applied. When you're writing in a more procedural style, generators are your "do syntax" equivalent(-ish): async(function* getCredentialsByToken(token) { credentials = yield NewCredentials(token) return User.findByName(credentials.userName) }); ES6 is a lot more powerful than it looks :)
- empthought 13y agoThis isn't a question of "my style" -- zsombor's notation is objectively better. It achieves the same results with fewer tokens and less intellectual overhead. The difference between "functional" and "procedural" style here is nothing more than monadic bind operator vs do-notation, but what you have above or in the initial response is nothing like that. Also I'm not sure "arrows" -- http://www.haskell.org/haskellwiki/Arrow http://www.haskell.org/haskellwiki/Arrow -- means what you think it means in the context of functional programming? It's not sugar for `function(){}`...
- spion 13y agovar just = property => object => { var o = {}; o[property] = object[property]; return o; } Credentials.get({token}) .then(just('username')) .then(withThis(User, User.get)); withThis available in a comment below.
- ufo 13y agoWhile I agree that using callback parameters instead of variable assignment is a bit fugly I think its not that bad and just takes think some time to get used to. For me, the thing that really benefits the most from generator syntax is structured control flow, specially loops: if you use generators you can use the existing JS for and while loop which is much better than doing the same with raw callbacks (manual recursion is basically gotos) or a an async library (promise-based or not, its going to be very verbose and still won't play well with "break" and "return" statements).
- deleted 13y ago[deleted]