Hacker News new | ask | show | jobs
by jenius 4148 days ago
So I've been writing javascript full time for a couple years at this point, client, server, and open source, and what I have adopted is coercing everything into promises, which I suppose would be the author's way of saying making everything red.

If you have something that is not async mixed in with something that's async, you can still add it to the promise chain and it will resolve right away. If you have a library that uses callbacks or some other thing, you can just wrap it such that it now uses promises. And then of course you can always look for alternate libraries that use promises from the start and skip step as well.

I've found that using promises for everything works super well. There is no confusion or doubt at all. Everything has the potential to branch into async at any time with no consequences and without complicating the flow. And an additional benefit is that rather than checking for errors after any operation you do, you choose where to check for errors. When a promise rejects, it skips everything else in the chain until it gets to a catch. So rather than running 4 async operations and doing an "if error do this" after each operation kind of deal, you can catch the error in once place and handle it once. Promises surpress the error in the promise chain until you choose to handle it, which is dangerous if you don't understand how promises work, but really useful once you do.

There are really solid promise-based libraries for all common operations in node right now. When.js for general promises, composition, and coercion, rest.js for network requests, bookshelf and knex for database connection and orm stuff, etc. If you are a js developer, give them a shot!

Don't get me wrong, I'm not trying to claim that this is better than any other language-level construct by any means, but if you are working in javascript, where you have to deal with javascript's limitations as a language, from experience I can say that working in an all-promises environment makes things quite pleasant.

2 comments

This is great for one's own projects, but if creating something for more than one's immediate project (i.e. libraries), it forces everyone else to adopt the same style.

Maybe those other projects are also using other libraries that don't use promises, so now there is a problem. Do you wrap the other library in promises too, if that is even a viable option for you?

Colorness is a problem for the whole ecosystem too.

Promise is the best thing an async library method could return because it's always trivial to convert into anything you want (callback, stream, async/await, yield whatever you want) because it's the only thing that's standardized. However, nobody gets the callback contract (https://gist.github.com/CrabDude/10907185) right. Even node core gets the callback contract wrong in different ways in its different APIs.

In practice however most library callback apis resemble the callback contract enough so that one-line promise-wrapping of the entire library is possible.

Maybe I didn't get this across in my original comment, but my primary job is open source software. So I spend ~40h/week working on public projects that are not all my own. I spend tons of time contributing to other peoples' projects, and I authored and maintain a significant number of libraries that are used by other developers.

What I was saying is that it does work. I can use dependencies with no problems no matter what they expose by coercing to promises internally, and have had no complaints about my libraries' APIs use of promises.

I agree that colorness is a problem, but there are also fantastic libraries that handle easier conversion between most of the common styles. For example, `bluebird` promises allow you to swap between error first callbacks in both directions with `promisify` and `Promise.nodeify`.
I agree. I think that there are two things that are missed out from the this article.

1. Programmers are lazy and will waste cycles like crazy around IO if given a chance. Core library async get's them thinking in red mode but it also get's them actually thinking in ways that allow the overall app to usefully get on with other things. Go sounds like it might be a nice exception to this but I'm not familiar enough to comment.

2. He kinda skips over the fact that threads are really really hard to work with and test (I immediately think about wasting time with race conditions that are just about impossible to reproduce or fix). Node.JS style continuation hell has the advantage of being a model that can actually be understood and made to work reliably in a way that threading, in theory can, but in practice cannot.