Hacker News new | past | comments | ask | show | jobs | submit login
Bye Bye JavaScript Promises (sriku.org)
144 points by tosh on Oct 1, 2014 | hide | past | favorite | 56 comments



And thus does the javascript ontogeny finally finish recapitulating phylogeny, and we arrive at the inevitable end point: The Erlang/Go way is the best way to do this stuff after all.

If you still end up using Javascript at the end of the process, hey, great, go nuts, but after years of swaggering optimism in the JS community about how somehow their stuff was so much better than anybody else's despite "event-based code" being extraordinarily well-trodden ground since the 1990s (as in being the default programming model for all serious code and entire major dominant operating systems), it might be worth people taking a moment to consider whether some more listening at the beginning of the process might have cut some years off this journey of discovery. The Javascript community has not been leading the charge of writing asynchronous code... they just caught up.


The erlang/go concurrency model is the most overrated paradigm ever.

Sure, the abstraction isn't leaky... Because there is barely an abstraction at all. Synchronizing state between independent processes is a very real, very unfun problem when all you have is the actor model.

For what it's worth, all of Facebook.com fetches its data with async/await (effectively promises+data) which is a nice sweet spot and a lot better to work with than actors.


Go uses CSP concurrency, which is distinct from the Actor model. They're often conflated since the difference is small, but the distinction is relevant here because it directly addresses your complaint. Namely, synchronization is much easier in CSP since communication is synchronous, so you don't have to build additional abstractions for syncing on top of your asynchronous message passing.


CSP is better for sure, but message passing is still a pretty primitive model, synchronous or no.

FWIW I like CSP, but promises are great for e.g. fetching data


Also ClojureScript core.async uses the same CSP model.


Erlang processes (actors) are synchronous as well.


SO are we talking about server side Javascript? I guess so, because Erlang/Go are not an option on the client side ...


At this point, for all the work that has been sunk into JavaScript, we could have embedded a superior language in the browser, many times over.


Amen to that.


Wow, what a mean-spirited comment. You should seriously think about why you hate a community as bad as you obviously do.


That was not a mean-spirited comment, especially if you imagine someone with a bit more experience seeing the same thing recur needlessly. Grouchy with a touch of get-off-my-lawn, yes. Mean-spirited, no.

Over-exuberance sometimes requires admonishment, especially when there is such a colossal waste of energy and effort like in the JavaScript "community" (OP's word, but still...). If JavaScript programmers spent more time listening and reading and less time posting "so I wrote my own" blogs we might have had things like Reactive libraries for JavaScript years ago.

Throwing a word like "hate" around tells me you're likely too young to really understand what that means.

Also, you picked on his tone and let the Go comment slide? Really?

In short, get seasoned, dude.


Thank you, thank you, thank you.

Even I, (young, unseasoned) know that criticism, admonishment and even a little "I told you so" do not equal hate.

Ugh, that word.


If you can't tell the difference between opinionated and mean-spirited you may be reading in the wrong venue, here.


Bluebird also solves 2 and 4:

2. Promise.prototype.bind 4. You can put try / catch / finally anywhere. It doesn't have to be in the end, but it does have to be close to what you want to wrap (just like any other try / catch).

Coming form a large node project where I have been spending the better part of my waking hours for several weeks closing memory leaks caused by holding onto closures indefinitely, I would advise against the sort of meta programming advised in this article.


Promises + ES6 generators (Promise.coroutine() in bluebird) solve all of these problems.


Indeed, I'm surprised the author doesn't know about them (or atleast doesn't mention them). Especially since even the async-await strawman (the first thing you'd find when googling for async-await in JS) demonstrates an implementation via generators...


https://github.com/srikumarks/cspjs - The readme has benchmarks from bluebird generators. It is only 74 ms slower than the sweet.js solution. So the author knows.


Look at the author's post on using promises. The author does not understand promises very well yet.


Generators are up there with the let keyword in the list of "things I wish I had (native) access to already" because there's just so many use cases for them. This is definitely one combination I haven't thought of before though, so thanks for bringing it up!


Is this similar to the co utility? I've been using it to yield partially-applied node calls directly, no promises required.

    var text = yield fs.readFile.bind(null, '/file.txt')


Yes, it's similar, `Promise.coroutine` is faster and a stronger abstraction though, especially with `addYieldHandler`.


They work together beautifully, i wrote this little prototype/promise helper for node (https://github.com/icodeforlove/node-promise-object#deferred...). And it removes all of the boiler plate associated with using promises, and generators (if you're ok with argument stuffing).


Will promises still be needed with generators?


They work together very nicely. See taskjs (http://taskjs.org/) for an example. ES7 is considering embracing that pattern with some syntax sugar (http://wiki.ecmascript.org/doku.php?id=strawman:async_functi...).


Generators dont give you async programming capabilities.You need a library that actually wraps generators into coroutines,and then wrap async functions into "thunks" to make it work.


yes generators can help you control the flow as you do async programing, but you can't use generators to do something like an ajax function which gives you an async value, you'd need promises or something for that, generators allow you do use those async function is a more natural way.


Promises _are_ an async value. By yielding promises you're doing exactly that :)


JavaScript and C++ have a similar problem. There are so many ways to do so many things, that the result is endless stylistic fragmentation and tons of library bloat. C++ is getting better as C++11 introduces standard ways to do things like lambdas, tuples, etc., but JavaScript is obviously still in its experimentation phase.


IT's very easy to criticise a technology when you're not using it correctly. Please consider reading the Bluebird usage examples, what you're doing is _definitely_ not the recommended usage.

You can achieve what you're doing without any macros, with better throw safety, better debugging traces and more.


It seems like there's two ideas here: new error handling syntax, and new asynchronous task handling syntax. About asynchronous tasks: I think many people here might be interested in taskjs which combines Promises and ES6 generators (http://taskjs.org/) and the similar ES7 async function proposal (http://wiki.ecmascript.org/doku.php?id=strawman:async_functi...). You can use await on a function that returns a promise and write code just like the code in this article (besides the error handling stuff). In basic sequential code, you never have to call .then() on a promise, but you're still able to dive down and do that if you have some non-linear asynchronous code that requires it.


While reading this article, I remembered this: https://github.com/jlongster/js-csp, which explicitly relies on es6 generators. I wonder if anyone can help us out by comparing and contrasting this to the subject of the article (https://github.com/srikumarks/cspjs).


tldr; macros

Yes but No.I personally think macros are the "root of all evil". Because they just make languages unreadable. That's one thing to have a complete different language that compiles down to JS,but as soon as you introduce macros is your code you make it harder to understand and document.I'm glad it works for you,but in my opinion the use of macros should be banned.While limited to nodejs, I personally think fibers are better alternative.

4. I actually think that it makes promises elegant. You can chain 20 then and have a single catch in case things are wrong,but you need to adjust the logic accordingly.Using different Error "classes" and checking the type is usually what I do.

I still believe promises are an elegant way to deal with async programming,and ES6 lambdas will make them even less painfull.

I'm not sold on generators for async programming strangely ,for multiple reasons(lack of support in browser land,especially on mobile).

EDIT: macros are fine in LISP likes because it's not like these languages have a complex syntax. And in C,well you cant program without them. My point is javascript has enough features to make macros unnecessary.


I don't think you understand the ability for macros to make code more readable.

"However, as we confront increasingly complex problems, we will find that Lisp, or indeed any fixed programming language, is not sufficient for our needs. We must constantly turn to new languages in order to express our ideas more effectively. Establishing new languages is a powerful strategy for controlling complexity in engineering design; we can often enhance our ability to deal with a complex problem by adopting a new language that enables us to describe (and hence to think about) the problem in a different way, using primitives, means of combination, and means of abstraction that are particularly well suited to the problem at hand."

https://mitpress.mit.edu/sicp/full-text/book/book-Z-H-25.htm...


see the edit.I still think they are unecessary in javascript.


I've used C a lot, and while you do need macros sometimes, IMHO it's best to use them as minimally as possible. So I'm in agreement.

lisp/scheme have macros that understand the structure of the code in a more fundamental way; they're more a feature of the language than a pre-processing step.


This seems overly restrictive. Especially when, at a base level, macros should be easier to explain than some alternatives. It is quite literally, "take this and replace it with that."

Don't get me wrong. They can hurt a codebase such that it is not readable anymore. But you know, functions can do similar. Heck, for some code basees, the same thing.


I'd argue that sweetjs actually changes JS syntax,which is not the case with some other languages that have macros. If JS had Ruby's blocks, there would be no need for sweetjs. But I've heard TC39 wants to introduce macros in the future,so you might have them anyway...


Ruby blocks are a poor substitute for a macro system. JavaScript already has something better than Ruby blocks anyhow: first-class functions.


Ruby certainly has first-class functions. Not only can you pass "functions" around as lambdas or procs, you can also detach and re-atach methods from a class or object at runtime.

From the wikipedia:

"Specifically, this means the language supports passing functions as arguments to other functions, returning them as the values from other functions, and assigning them to variables or storing them in data structures"

All of these things are certainly possible in ruby, see [0] fizzbuzz in the lambda calculus in ruby

[0]: https://gist.github.com/tomstuart/1466504


From the same Wikipedia article:

"The identifier of a regular "function" in Ruby (which is really a method) cannot be used as a value or passed. It must first be retrieved into a Method or Proc object to be used as first-class data. The syntax for calling such a function object differs from calling regular methods. Nested method definitions do not actually nest the scope."

You can certainly make the case that Ruby has first-class functions, but they don't feel very first-class in practice.


Some consider Ruby blocks to not be first-class functions since calling them is different from calling methods.

I don't know if I agree or not.


"Bare" Ruby blocks are called differently from calling methods (yield).

But the moment you name a block (pass it to a method with a named block argument) or create one with "proc" or "lambda" or "Proc.new", the form you get access to it in is a Proc object, and calling it is a method call (ob.call).

The distinction is quite pointless, other than as an implementation artefact for MRI, as there shouldn't be any obvious observable differences in behaviour between a bare block and a Proc (in my "eternally in progress and not very functional yet" Ruby compiler, all blocks are in fact Proc instances, though if you can tell, it's a bug)


It's interesting to me that people seem to be trying to bridge Promises with CSP. Why aren't more people investigating the more straightforward async/await transformation that the C# compiler does? CSP just seems like one transformation too far.


async/await is trivial to mimic with yield - run the outer-most generator in spawn, use yield* with "async" methods, and use yield with Promise-returning methods. Projects like Regenerator already know how to convert yield to straightforward JS. So you already have the transformation the C# compiler does.


It works like this in ClojureScript.


async/await is an upcoming JS feature that actually is kind-of supported by Bluebird when combined with generators. The author just didn't know about it (I guess) and reimplemented it via macros.


I'm sure the JS world will just cook up 3 or 4 libraries to bolt on the desired functionality...


...each of which will have distinct bugs, leaks and gotchas. All of which will be incompatible and conflicting with one another.


A bit off topic but in the context of promises and Angular, I love the ability to set a set a promise for the timeout parameter. This is super valuable 'cause it lets me manually cancel any request instead of waiting for a response or timing out via time.


If you'd like - we have a $q decorator in our code base that adds `.timeout` to promises and allows you to cancel them if the timeout expires (just like in Bluebird, from before we used Bluebird with Angular). Let me know if that's interesting to you and I'll put it on GH.


I would be interested in seeing it posted~


> I love the ability to set a set a promise for the timeout parameter.

please explain .


You can pass a promise into an $http request as a parameter. If you hold on to this promise and resolve it before the request returns, it cancels the request.

https://docs.angularjs.org/api/ng/service/$http


I assume the grandparent is talking about functions in Angular that take in a numeric timeout parameter and wants them to take in Promises instead, so he can reject the promises to signal that he wants to cancel the operation.

Kinda like CancellationToken in .Net, I guess.


There seem to be two major ideas going on here: CSP, and modifications to try/catch syntax. It would be easier to understand the former without the latter.


The 'code' blocks on this page are all using variable-width fonts, making it quite difficult to read the examples.

Consider changing the 'font-family' to 'monospace'?




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: