So I am curious why you consider it extremely valuable with such stable sugars in the first place? I mean I can see it could be nice to have for the stats but that's not worth the obtrusiveness at all.
655 karma · joined June 18, 2012
So I am curious why you consider it extremely valuable with such stable sugars in the first place? I mean I can see it could be nice to have for the stats but that's not worth the obtrusiveness at all.
Example: Average code written in node.js is typically just as slow as, say, PHP (in fact node.js was 3x slower than php in techempower benchmarks). However, it has potential to be much faster when optimized whereas PHP doesn't reward such optimization almost at all.
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.
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.
There is just no way to use yield without it either being superfluous or sacrificing concurrency. Of course it is great when you have a sequence I guess, but promise code with arrows is not messy in comparison, maybe slightly more verbose at best.
Consider something like this which is far from optimal:
let suspend = require('suspend'),
request = require('request');
let getParsed = suspend(function* (urls) {
urls.forEach((url) => request(url, suspend.fork()));
return (yield suspend.join()).map((r) => parseBody(r.body));
});
This is again very easy to write inadvertently.Maximizing concurrency:
let getParsed = Promise.coroutine(function* (urls) {
return yield urls.map((url) => {
request(url).spread((response, body) => parseBody(body));
});
});
However, now that we have maximum concurrency, it is entirely pointless to even have a generator: let getParsed = urls =>
urls.map((url) => {
request(url).spread((response, body) => parseBody(body));
});
});
I said try-catch silences all errors that are not event meant to be handled but are bugs in code that should never be thrown in the first place. For example, if you have a typo in your code you will not know about that instead you will handle it like an expected error like network error.Since promise `.catch()` is not limited by the language, you can do e.g. `.catch(NetworkError, e => ());` or whatever.
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. #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 })And language benchmarks don't tell the whole story. In Java, idiomatic and typical code will be optimized very well whereas in Javascript such code runs easily at least an order of magnitude slower than its potential. Try to run some idiomatic JS through https://github.com/petkaantonov/nodeperf/ and see it explode :)
So even if Javascript can be shown in some benchmark to close on the JVM, the benchmarks are ignoring the fact that you cannot write Javascript carelessly to get anywhere near those speeds unlike with Java.
Benchmarks with N=1 are complete bullshit and you cannot use it at all to reason what will happen with a different N.
Another annoying issue is how code is written. Well optimized code will still perform like shit in interpreters like PHP while typical JS code I see can be improved substantially (10x-100x) by writing code in a way that V8 expects. This speedup potential is usually completely ignored.
The implementation by facebook (HipHop) might have such an optimization though.
Although with generators you "can use normal control flow constructs". Except in most cases you probably use map or forEach instead of loops anyway. And try-catch is really broken.
<div>@hello</div>
The generated code can be like (not including runtime lib): var ___html = "";
var hello = hasown.call(data, "hello") ? data.hello : "";
___html += '<div>' +
safeString(hello, HTML_GENERIC) +
'</div>';
return ___html;
Which gives same effect as `with`.I am sure you realize you can just walk the AST for what identifiers are being used in the template code and forward declare them in the generated code so that reference errors are not caused.
If such a petty and trivial "problem" is what you have against Javascript, then you are basically saying it's a good language :--)
You should read http://zedshaw.com/essays/programmer_stats.html
If the OP wanted to measure function with or without return , even without realizing how futile that is, they still should not include things like jQuery.
However, on node.js you can have more than 1 client served by your server. For instance, your server could have e.g. 10000 promises/callbacks/generators active at a time. Take a look at how different solutions handle that https://github.com/spion/async-compare/blob/master/latest-re....
The results don't change at all if we for example increase latency of I/O by an order of magnitude - that's why latency is not an interesting metric for this use case. It's pretty intuitive, all the 10000 users will just get their results 9ms later but the fact that we could even handle them in the first place without crashing our server matters.
----------
You are right that for your use-case, pretty much the only thing that matters is what implementation is used for queuing async calls since it's such a tight bottleneck. Bluebird attempts to use mutation observer if available.
Also, in your benchmark you do output in bluebird section with console.log. Maybe try http://jsperf.com/promise-comparisons/50.
$ node --trace_inlining go_when.js
Did not inline fulfilled called from coerce (target contains unsupported syntax [early]).
Did not inline NearFulfilledProxy called from fulfilled (target requires context change).
Did not inline near called from fulfilled (target contains unsupported syntax [early]).
Did not inline Promise called from near (target requires context change).
Did not inline enqueue called from scheduleConsumers (target requires context change).
Did not inline b called from enqueue (target not inlineable).
Did not inline fn called from NearFulfilledProxy.when (target requires context change).
Also some functions are constantly being recompiled and try catch is corrupting (making them not optimizable under current v8) some large functions instead of being isolated.