-
Notifications
You must be signed in to change notification settings - Fork 12
NG should be idiomatic JavaScript #14
Comments
👍 for all of this. I worry a bit about NG becoming the new Python 3, with such drastic changes on the table. But I think the ES6 way is so clearly better that it hopefully won't come to that. |
@Qard It at least has the benefit of not having to solve these problems itself. Others will both solve and experience the pain first. |
We also have semver, so there's that. |
-1 callbacks -1 async await -1 to classes |
No need to use, it's enough to return promises
Too early for that.
This works already, you can extend those with ES6 class syntax. |
This is NG we're talking about, not mainline. If something looks like a thing that could be a solution in the future, it should be discussed. This is not an absolute task list. Also, async/await is a fairly developed proposal, which people are already using via transpilers, so I think it is quite likely it will become a thing people use regularly soon. |
@Qard yes but some people w/ experience using async/away are moving to compositional functions due to issues. Those issues are still in question and are not getting traction to my knowledge. As for the other stuff, moving to some things does not make sense, even if it is possible it might not be a good idea. |
using promises is safe and sound at the moment, and since they will be able to be |
We also have https://github.com/zenparsing/async-iteration . |
I don't like the way this is being framed. First off, just because a feature in landing in the language doesn't mean it becomes "idiomatic" JavaScript. Is What is idiomatic is whatever the community adopts. This puts the platform in an odd position because it sets patterns that will likely spur adoption of whatever features we decide to adopt. If these patterns gain a lot of traction in the frontend we can safely assume they will be idiomatic once they are also supported well in the platform but my guess is that we'll end up leading here rather than following. You'll recall that a sizable community spun up around generators and is now slowly dying off with even some of its proponents now opting for promises + async/await. Still, that was a feature in the language, people may have even started to call it idiomatic, and if we had implemented it in core we would have been wrong to do so. Honestly, I'm cautious of any pattern that doesn't have a dozen or so articles talking about where it sucks and the problems it can cause. Any pattern or abstraction if you use it enough will have issues, deciding which ones are more or less important and understanding the tradeoffs is how you make good decisions. Because some of these features are still in draft specs and cross compilers the people adopting them widely have a high tolerance for living on the bleeding edge and so it'll be some time before people more honestly talk about the warts and we can have a good discussion about the tradeoffs. A good example of this is promise errors. I could only get promise supporters to admit the issues promises have with errors (silent failures being quite common) in dark rooms in private for years. Nobody had written an article about this that I had seen, it was just this dirty little secret. It wasn't until v8 supported them natively and we had them in io.js did I see people discuss this issue honestly and, very quickly, a solution was proposed, implemented and shipped. It just shows that we need time with these features, not just in the ecosystem through cross compilers, but "native" in the platform even if not in use by the standard library, before we understand them enough to discuss them honestly and evaluate the tradeoffs. |
That all said, Promises appear to be well on their way to being "proven." The userland promise modules are some of the most depended on modules in npm, frontend tooling has started adopting them widely, and the native implementation in io.js has been progressing nicely. If the performance and debugging issues can be dealt with in v8, and @domenic is confident they can be, they'll need to be a big part of any future platform. I still hate them personally, but I'm in the minority and even I can't ignore how much better they've gotten. |
@mikeal I would love if we had a v8 vm implementor comment prior to talking On Mon, May 11, 2015 at 11:22 AM, Mikeal Rogers notifications@github.com
|
We have had some internal conversations about promise perf, and though I don't want to quote directly since they were internal, I can say that the consensus is that the current implementation is written without any attention to performance, that there's a lot of low-hanging optimization fruit, and that with some effort it should be trivial to meet or exceed Bluebird-level performance. |
@mikeal I hear you and echo most[1] of your perspective. This issue is not about language features being de facto idiomatic. It's about avoiding a node-flavored versions when language-first versions are (will be) adopted by the majority (assumption) of the community. I've added an "Edit" to the OP to clarify. 1st class language implementations are typically superior in that they tend to be faster and widely adopted. Despite the backlash against class CapsLockStream extends Transform {
constructor() {
super()
}
_transform(data, encoding, done) {
for (let i = 0; i < data.length; i++) {
if (data[i] >= 97 && data[i] <= 122) data[i] &= ~32
}
this.push(data)
done()
}
} instead of... function CapsLockStream() {
Transform.call(this)
}
CapsLockStream.prototype._transform(data, encoding, done) {
for (let i = 0; i < data.length; i++) {
if (data[i] >= 97 && data[i] <= 122) data[i] &= ~32
}
this.push(data)
done()
}
util.inherits(CapsLockStream, Transform) Teaching the latter over the former to new developers is a fool's errand, and I suspect the experts (meant sincerely) that are core developers are years or decades removed from the mental hurdle of understanding function constructors, a hurdle that is largely responsible for the node idiomatic If this were Io, one could argue it's "too early" or "backwards breaking," and it would be a recurring frustrating refrain, but this is NG. Biases should be put aside in pursuit of objective bests. Assuming community adoption, in the case of This issue is largely to encourage and discuss rebuttals if they exist. To avoid being "born derailed," please note no mention of That said, are there any arguments for
I'm ignorant here. Could you elaborate?
This is a special case, and the documentation favors extending
See [2] below.
This seems like a straw-man(?). Neither callbacks nor promises are cancelable. The proposition is that [1] I don't know of anyone that seriously believed async generators to be anything other than an incredibly useful hack. They were far from idiomatic in that they were neither the language's intended purpose for their use, nor had the asynchrony (yieldable) yet settled on [2] For those who know me, I was vehemently anti-Promise for the first 3-4 years of node.js programming, and only after my many experiences building node applications, teaching thousands of developers, and attempting to solve async error handling with |
Compositional functions : https://github.com/jhusain/compositional-functions Basically complex workflows have struggled / not help up w/ Promises as the backing controller for async await for various people.
The main issue here is you cannot have something be both
The problem however comes from a matter of overall gains vs losses, I tried to move to async/await and lockfile usage and lack of abort made me make my own library to work w/ a more flexible system. I am completely against promises as control flow; in particular without having a cancellation mechanism / Lets take a moment to see how we use cancellation at work, but have had to avoid async/await to do so:
|
I should probably write a browser plugin that replaces word
Not really. See "native promises vs bluebird" above. In fact, all new es6 features tend to be slower than old equivalents, because nobody bothers to optimize them just yet.
It is also far cleaner and reader/learner-friendly to write javascripts without semicolons. That's the same level of purely style-related bikeshedding. Not that I'm against that, but still.
AFAIK, fs.readdirSync(__dirname).forEach((file) => {
module.exports[file] = require(`#{__dirname}/#{file}`)
}) Though some people argue you shouldn't do it with
I do seriously believe that async generators are superior to promises and callbacks right now. And I do have a private project written purely in async generators, even though public ones are still using promises/callbacks for legacy reasons.
I don't believe anything could be worse than silent failure. If your process crash, you could notice and fix it, but if your process just ignores errors and stops executing midway, there's nothing worse than that. Fortunately, recent introduction of Callbacks on the other hand work fine and always did. Only disadvantage is they don't work well with high-order functions (e.g. when you monkey-patch a generic async function, you need to do a rather complex stuff to get a position of a callback in I wish fs.readfile('/tmp/foo')(function (err, res) { ... }) It would be the same idea as promises, but without needless |
There is loader API for this. |
@CrabDude NB, your second stream example is not quite accurate – we recently grew WHATWG-style constructors: const txf = new Transform({
transform(data, enc, ready) {
return ready(null, data.embiggen());
}
}); I would heartily suggest avoiding teaching newcomers that inheritance is the proper way to interact with the streams API – and even more emphatically suggest avoiding subclassing event emitters. |
@bmeck @rlidwka @chrisdickinson @vkurchatkin =) Lots of constructive feedback makes me happy.
I'm not crazy about inheritance, and prefer the simpler
Unfamiliar. Reference?
Thanks for the reference.
Consistency with the ecosystem when there is no benefit otherwise. It seems this is being confused at least a little with an assumed superiority of language implementations. WRT
Iit would be inaccurate. This issue is explicitly about functionality and whether exploring whether node-flavored versions provide, not style.
You missed the labeled assumption in the OP.
This is purely stylistic, and unrelated to node.
As do I, but that wasn't my point.
Silent failure exists with callbacks as well and is arguably more common and error prone through manual propagation.
Promises provide chainability, caching, automatic error handling and a whole suite of features that callbacks and thunks don't offer. |
Async/await are a little better, but may be confined to situations that actually set us back from callbacks which is a big red flag to me; I leave my work in userland until they can deal w/ resource cleanup / lifecycle (lockfiles are a classic example). Also, remember that caching is not always what people want / you need to be more careful to avoid memory leaks. |
WHATWG/streams. Also, the snippet in my original comment shows how that operates in practice (you can provide a |
Totally agree!
|
@bmeck have you seen bluebird's resource management features? They can easily be written on top of native promises. Are they not good enough? |
@spion they are technically enough, but they require code an using both |
Why can't that be written with async foo(abort) {
let release = await lockfile('filename.lock', abort);
try {
db.query(...);
fs.write('filename', ...);
...
}
finally {
release();
}
} @hax Unless I'm mistaken, those both exist within the V8 heap, while |
@CrabDude there is no way to abort the result of
will return |
@bmeck Ah. This is very interesting, and as you say "complex". Equivalent functionality could only be accomplished by checking compromised at every call to |
@CrabDude https://github.com/bmeck/generator-runner is what we are using right now, but more work needs to be done wrt. this before going all in and saying it is a good way to do things. |
@bmeck bluebird 3.0 will have some interesting new cancellation semantics. Not sure if they'll do what you want. |
Yes 3.0 cancellation works with generator cancellation ( Although it wouldn't affect the example since it doesn't have a catch statement, but if you did, you'd have to manually write the boilerplate check at the start of every catch block: catch (e) {
if (e === coroutine.returnSentinel) throw e;
} |
Sorry for not contributing anything positive to this thread, but I didn't want to miss the chance to say publicly that I TOTALLY agree with @CrabDude |
With ESNext, there's a whole suite of new functionality previously provided by core. Given the opportunity for breaking changes, NG should strive to be idiomatic JavaScript in all regards (EDIT: Language features are not de facto idiomatic JavaScript, but they are likely candidates):
Some obvious candidates:
async
require
in favor ofimport
async/await
focused documentationclass
forEventEmitter
,ReadableStream
, etc...Distant future candidates:
I'm sure there are other new language features that could be added to the list. Also, for the sake of discussion, assume a stable performant finalized implementation exists for all of the above in V8 without a flag.
EDIT:
The text was updated successfully, but these errors were encountered: