Node.js v7.0.0
github.com
github.com
The whole promisify is a good example of general node.js culture. It's considered "good enough" and everyone just uses it. However, anyone who has done software development in more solid languages would feel very uneasy using something like this. It's not really the fault of the JS developers, the dynamic nature of the language offers no other choice. For example, when Java added lambdas, since it has static typing it could consider a certain type of class (a single method class) as automatically a lambda. This allowed full backwards compatibility with any old library that conformed to this (and many libraries such as Guava did use single method class as a kind of "ugly" lambda).
node.js has a convention for callbacks (function(err, result)), but unfortunately it's only a convention and there's no compiler to enforce it. So automatic promisification is not possible. That leads to the current situation. There's all these new language features in JS, but everyone is still sticking to the "lowest common denominator" of callbacks. There's no path forward for existing libraries. The only way is a complete rewrite of libraries using the new Promise/async/await style.
Thats also one cool thing of the js/node community. We just adopt stuff and try it out. With microservices, this also poses no problem. If it doesn't work, do it differently in the next service.
[1] I know, I know. A+ Promises are violating some laws BUT the general idea holds. And there are full monadic promise libraries like Data.Task or Fluture available.
The sounds like a limitation of promisify, not inherently of callbacks. There are many situations and styles where callbacks are perfectly adequate, and promises offer no benefit, so referring to it as the "old callback style" may be a bit premature.
Promises are superior in general, I guess that has been discussed already[1], many times. Promises may indeed offer just an insignificant benefit sometimes (you can even call it just a different style), for example when you are first writing a monolithic prototype. In this worst case, you would simply make a giant chain of ".then" methods, any your gain is you can get creative with the error handling if you want. However, the greatest benefit revels itself later: The composability that comes with them will help you immensely when refactoring.
[1]: One example: http://softwareengineering.stackexchange.com/a/302456/9451 Another: http://blog.parse.com/learn/engineering/whats-so-great-about...
Performance may be a tradeoff for the current engines, but I'm optimistic that it will be optimized away as it doesn't add too much semantics over the callbacks. Maybe error handling is tricky.
I would say they are superior to callbacks as object-oriented code is to procedural code - that also has some performance cost in many engines.
But, you are saying tradeoffs - plural. I, as an experienced web developer, am sincerely curious what any other can be?
That is not a universally-accepted statement either.
I'm not interested in taking sides here, only pointing out that there are a wider variety of extant opinions than you seem to realize.
Maybe we can all agree that experience in other languages leaves ECMAScript with much to be desired. Whether the changes ES6 hath wrought and ES7 and friends portend bode ill or well is likewise up for debate. So are the one-trueness of futures, specialized async syntax, and static typing. Those aren't either-or kinds of debates as far as ECMAScript is concerned. ECMAScript could be more Scheme-like, more Java-like, more ML-like. I've forgotten to mention some other disappointed camp. Forgive me!
If specific judgments on those kinds of controversies unify a culture, there is no unified culture of Node.js, any more than there is a unified culture of C or Java or English punctuation. The only unity is that standards-compliant ECMAScript runtimes are everywhere and we all want to use them. As long as different tastes are at least sufficiently well specified, we've a fighting chance of automating adapters. So I'd argue promisifiers and depromisifiers might be the most important libraries of the moment, culturally speaking. But it's too much to ask of any broad and adaptable common platform or library to hide the fact that tastes and approaches differ.
Fixed that for you.
Maybe he was right all along, but was actually talking about scheme.
(I can't just let go that it was soooo close that we would not had to suffer in the javascript wastelands.)
I have a few reasons why node-style callbacks are absolutely the worst possible choice, so I'd like to compare.
Here are some:
1. Not values. Values can participate in expressions, can be passed as arguments to functions, can be returned as values from functions, can be stored in variables, arrays, etc. There are no values with node-style callbacks. You are throwing away half of the language capabilities regarding values and expressions.
2. No guarantees. Callback may be called multiple times. It may be called synchronously or asynchronously. There is no way to know what will happen.
3. Bad semantics for thrown errors. Its assumed that thrown errors will simply crash the process. This is a dealbreaker in node because every uncaught thrown error provides a vector for denial of service attacks, and the thrown error semantics of most libraries and the standard node library are pretty bad.
I know most people use Promises to escape the call-back hell, but you can do that with async library as well, so maybe you just like writing "return cb(err);" more than "return Promise.reject(err);"
Maybe you like, that you need to deal with the err in the call-back, while \w promises it is easy to write code that just silently fails?
- They are simple (fully transparent)
- They are the simplest good enough solution for many problems
- Cleaner code than new Promise().then().then().then().catch() clutter
- Yes, I like nested indentations (good functions are short anyway)
- They perform well
As a side note, I also prefer coffeescript, snake_case, CONSTANTS, and avoid using classes, prototypes, and this. I'm not very happy with the recent development of EcmaScript, too much complexity and unnecessary new concepts.And I'd like to know what you mean by "fully transparent"
I don't see how compile-time type checking would help here. When checking out a new library, I read the documentation, or maybe it's test suite, and then use it accordingly. My tests ensure that I use it correctly, i.e. give it types it understands, give it the right data (the id of the correct user account for example), and handle the outcome correctly (i.e. display it in the right format).
JavaScript has run-time type checking, which could be used for automatic promisification, but I agree that's a slippery road, for many reasons.
It's all good for prototyping, but man, once you're up in production, dynamism is a recipe for pain. Granted, statically typed languages crash too, but the big difference is that you can often eliminate the same problem throughout your program by getting the type right, as opposed to trying to TDD bugs one at a time as they emerge.
I think you nailed it when you said, "there's no compiler to enforce it". Actually, maybe a compiler isn't the key word, but automated checks is the idea. A type-checking compiler is just one solution, albeit a pretty powerful one. Type inference makes it much less onerous these days. But even staunch dynamic language devs have come around to embrace the use of linters. So while it's often seemed that static and dynamic language users would always be at odds, perhaps a convergence is slowly happening.
If you want promises, just embrace promisify. I get a lot of this fear about error-first callbacks being only a "convention", but everything follows this convention in practice. Anything that can't be promisified because of that will likely have a pull request or a more popular fork that can.
I don't think node core libraries should be promise-based, and you can't expect the whole ecosystem to switch to promises if node core won't. At some point you have to jump to callback-style to live on node.
I also don't really understand what this post has to do with the release.
We need something like promises to replace node streams too. We need streams with well defined semantics. Quick, what are the unpipe semantics for stream erors? Does a stream unpipe from its source? Will it unpipe synchronously or asynchronously? Can a stream emit an error synchronously when its created? At the next tick of the event loop? What about at the end of the same tick? Are these semantics defined anywhere? Which versions of node streams adhere to them? All is fuzzy.
We need to replace node streams and of all eventemitter-based streamlike stuff, badly.
Regarding types, you always have the option to add TypeScript into the mix.
Use $q.when() in your code if you aren't sure you're getting a promise.
Also you slipped in weasel language to disrespect JS developers. I have been coding for 30 years in wverything from assembly to C++ to C# to OCaml to now for the last several years mainly Node.js. Your comment was (reading between the lines) as close as you could get on Hacker News to spitting in my face.
Promisifying works fine. So do rewrites, as the language has evolved rapidly.
We do not actually have a big backwards compatibility problem.
Dynamic languages are dynamic.
You're just saying that if you make a mistake and use it wrong it doesn't work. That applies to all code, not just promisify.
> Also, the library writers are not writing the code with any guarantees that promisify will always work. So even if it works now, it could break in the future.
That also applies to all code, not just promisify. If the author changes their API without a major version bump, that can break any code that relied on the old API.
Promisify is deterministic and works as specified, 100% of the time. If it doesn't work, that means you passed a function with the wrong signature.
To my understanding, the following code examples do the same thing:
readFilePromise("file").then(... txt ...);
txt = readFileSync("file");
txt = await readFilePromise("file");
Why not use the one in the middle ?In a simple script you run from your shell, the middle one is fine.
I think it's perfectly fine to wrap standard nodeJS modules into your own module, Promisify or what not. Almost all NodeJS modules does actually use standard modules in some way, it would be very naive to suggest that the standard library should start returning Promises, as it would break almost every NodeJS module ever created.
I also think that Promises only makes sense when you only expect one return, it would note make sense to use Promises in NodeJS streams.
With await, you know. If there is no await, the call will not block, and more importantly, shared state cannot be modified from outside of that block of code. If there is await, there are no guarantees and you need to take the necessary precautions.
Kind of wish there was yet another number prefix to semver to signify this.
With semver, it seems like a lot of "new features" are typically released in minor versions, since quite often they don't need to break compatibility in order to introduce features. So major versions are, to me, almost more of a cause for concern these days. My first thought is typically "Oh no, what part of my stack is going to break now? How much time will I spend tracking down the fix?"
Isn't that exactly the point of semver?
And, assuming things will break at some point,* isn't that great? Now you know when to expect it.
Semver doesn't influence design decisions of a project's lifetime. It describes them.
* fair assumption, unless you're dealing with software which literally never breaks backwards compatibility.
SemVer is great and helpful and I wouldn't choose anything else currently, but it also lacks the builtin PR that old-school major versions seemed to have, where major version bumps usually meant you could get excited about exploring new major features. There's nothing special about a minor SemVer bump that says "new major features have been introduced." The spec only asserts that minor means new features.
That is, there's no obvious way to know that 1.1 introduced only one new method for checking status, while 1.2 introduced a new magic() method that finishes your work for you and makes all your dreams come true. :-P
Node.js V7
Node.js V8! (denotes something significant)
Also of note, chakra still has async/await behind a flag as well.
The line of "major" is arbitrary, and changes per person. It's basically useless information to everyone but the person/people actually making the version change. Having it there can just lead to anger when something that you would consider "major" doesn't make the cut, or when the "major" bump doesn't have anything you care about.
Semver is in no way the end-all-be-all of versioning schemes, but at least it's pretty objective for the most part in the sense that it can prevent bikeshedding about version numbers, while still giving some useful information to the users.
That being said, I'd love a system that lets me view changelogs by the version "level" I want. So I can easily lookup the major changes since v6.6, and someone else can check what has change since v4.2, etc... With Semver a lot of my time is spent reviewing every changelog between my current version and the one i'm bumping to, and there's no reason it needs to be that way.
If I release 1.0, then add a couple "medium" size features and release as 1.1 a month or so later, I can keep doing this indefinitely with 1.2, 1.3, etc. Baring some truly some new big thing or a fundamental change in functionality, it becomes very unclear as to when to release "2.0" or what even makes it different than the other 1.x releases.
On the other hand, if I do the exact same work of adding a couple features per month, but don't actually release, I could then make a big splash a year later with 2.0 that had 19 new features, and I think most people could readily agree that qualifies as a "major" release.
From a quality point of view, release early, release often is very useful: get feedback quickly, iterate in small chunks, minimize breakage.
However, from a marketing point of view, this is boring -- especially by the time you're on 1.19 and your features are largely quality-of-life or only affect a small segment of the market. A big 2.0 release that gets press releases and such is much more exciting.
Maybe version "names" could be helpful here.
Make a marketing "name" and use that as a way of showing off what's new, while still keeping semver true to it's name.
In your example, version 1.3 could be "Saucy Gorilla", while 1.8 could be "Delicate Apricot", and when you feel it's change enough from the start, you can pick an arbitrary point and make a new name and blog post.
It would still have some of the bikeshedding that older "major.minor" version schemes have, but it could be not as bad because it is more obviously arbitrary.
However, it seems Canonical keeps the codename out of their official press release: https://insights.ubuntu.com/2016/10/13/canonical-releases-ub...
Example: https://github.com/umbrellajs/umbrella/milestone/8?closed=1
For software, I would apply the rule as such: 1) merge the major and minor segments, and the revision segment becomes equivalent, 2) then you follow the Form, Fit or Function rule and apply it to the core product, programs, and APIs.
[0] http://www.arenasolutions.com/resources/articles/form-fit-fu...
[1] http://www.buyplm.com/plm-good-practice/plm-software-part-re...
You are saying basically have just `major.revision` correct? Would this be similar to how Chrome does their versioning?
I have software that is in production and has been for awhile. I've locked down all the versions of libraries I've used (FFF works here) and everything is running smoothly.
However, a large customer requests a new feature, and I see that library X has a new version that has new features that will support development for the new customer feature.
With semver, I can see that this new version is, say, 2.3.0. If I was using 2.2.x, I have at least some assurance that the API I was using before hasn't changed, so the amount of work to upgrade is likely limited to implementing new features, and not converting and re-testing older code.
However, with the FFF rule, it seems that the new version would change (due to form/fit (api?] or function changing), and all I know is that this version is new, and I have no indication how much work it would be to upgrade, other than assume the worst case that existing APIs have changed.
I could see why the manufacturing methods might make sense, especially in certified software... but in the wild west of web development, I have a feeling it would never catch on.
Part Name: MY SW 2.0
Predecessor Part Number: 150000-0001
Part Number: 200000-0001
And you have Part Name: MY SW 2.0-IBM
Predecessor Part Number: 200000-0001
Part Number: 200001-0001
Say your added features to your IBM branch caused some regression. So now you have a second build that is compatible but required a bug fix. Now you have Part Name: MY SW 2.0-IBM
Predecessor Part Number: 200001-0001
Part Number: 200001-0002
The biggest thing you should take away is that Enterprise Manufacturing and Inventory systems do not try to stuff all the knowledge about a product history into a single field, as SemVer attempts to.Our MIS vendor offers this, and it's indispensable. Especially considering that each of their customers are on a different version at any given time. You select your current version, and any other version to compare it to, and it spits out a report of all the differences from the module level down to the object attribute level, all nicely separated into logical groups. Due to the complexity of the system, I couldn't imagine a successful upgrade without it.
It just needs a front-end to slap on there, and maybe a bit of standardization to make it easy to pull this data from not just Node's docs, but from others that adhere to it as well.
> I'm glad to hear so much excitement about async/await. However, Node v7 is based on V8 54, whose async/await implementation has some bugs in it (including a nasty memory leak). I would not recommend using --harmony-async-await in production until upgrading to V8 55
source: https://github.com/nodejs/promises/issues/4#issuecomment-254...
The LTS versions are named after the periodic table of elements, starting at a and moving forward. First one was "Argon" (4.x), second "Boron" (6.x).
You can read more on LTS and naming here: https://github.com/nodejs/LTS
Joking aside, I wonder if there are any internal plans on keeping terminology clear for the next version?
- await/async behind flag (already covered in thread)
- Exponentiation operator:
> console.log(60**2*24);
86400
- Object.{values,entries}: > o = { a: 1, b: 2}
> Object.values(o);
[ 1, 2 ]
- Object.getOwnPropertyDescriptor(s): > o = { a: 1 }
> Object.getOwnPropertyDescriptors(o);
{ a: { value: 1, writable: true, enumerable: true, configurable: true } }
Additionally, there's a lot of pretty impressive optimization work done by the v8 team. You can read more about that on their blog: http://v8project.blogspot.comFinally, one of my favorite things about Node.js 7.0 is the WhatWG http parser: https://github.com/nodejs/node/pull/7448.
edit: elaborated on exponentiation operator
https://github.com/yarnpkg/yarn - 17,271 stars
https://github.com/npm/npm - 10,807 stars
It's in the works and slated for a later release, but makes it such that yarn is not a universally viable replacement for npm yet.
Actions like "copy this file", "clear this subdirectory", and "pull these files over the network" are easy to write synchronously in node, are there are modules for things that aren't.
For more complex actions, there are modules like env-cmd or cross-env for setting environmental variables on different platforms. If you had something really complex, you could check os.platform() and then call scripts written for each OS.
But you're right, you can't write a .sh shell script and expect it to magically work on Windows.
> there is no yarn equivalent to "npm run ..."
https://yarnpkg.com/en/docs/cli/run ? (I am not primarily a node dev, so I might be missing some nuance here.)