9 ms·
There is a lot to like about Node. I had a look a couple of years ago but lack of a definitive library to handle callback hell put me off. How is the situation
by SeanDav 10y ago
There is a lot to like about Node. I had a look a couple of years ago but lack of a definitive library to handle callback hell put me off. How is the situation these days?
- MehdiHK 10y agoWe've been using async/await (transpiled with babel) for last one year in our production API code. There really is not any need to use those async control flow libraries anymore.
- cookiesboxcar 10y agoAgree, except we use TypeScript as the transpiler. Async/Await down to es5. Works like a dream.
- pault 10y agoI've found that this works fine until you have a bug in your async function that is, after transformation, a promise and a generator away from your original function with no call stack available for debugging. Edit: My experience is with babel's generator version of async/await, so other transformations may be easier to work with.
- coldtea 10y agoThese days you can use: Promises, generators and even async/await.
- deleted 10y ago[deleted]
- deleted 10y ago[deleted]
- kozhevnikov 10y agoAdd --harmony flag to enable stable ES7 features (or transpile with Babel) and use async/await, it's marvellous.
- manojlds 10y agoNode 8.0.0 has it enabled without --harmony. Will be a great release to upgrade to.
- iso-8859-1 10y agoNode 8 has not been released.
- manojlds 10y agoThat's why I said "will be"
- lossolo 10y agoWhole standard api is still using callbacks so you would need to write your wrappers around it to not use callbacks.. And async/await is not solving anything because your definitions of fuctions will still need callbacks/promises under the hood anyway (unless you use libraries that do it for you). I like how Go solves that, gorutines and you write your code as it would be normal sync code.
- pitaj 10y agoThere is a V8 API to expose Promises directly from native modules, and that can eventually be used to make async functions first-class citizens. async functions using promises and generators under the hood is a good thing because it allows easy usage between the two forms.
- lossolo 10y agoThis still doesn't solve what I wrote about api, libuv (event loop + IO) that node.js use is also based on callbacks in C, last time i checked discussion about it, there was no plans to change that.
- Matthias247 10y agoThat simply won't change, because that's what node.js (and Javascript) is fundamentally about: An EventLoop, with callbacks that are used to handle events. All the promises and async/await are only sugar around callbacks, so they don't change the fundamental model. A Go like model, with lightweight or real threads and no need for callbacks or promises will also need new synchronization mechnanisms in order to handle preemption and waiting which are currently not in scope of javascript, e.g. mutexes, condition variables, etc.
- _puk 10y agoI imagine the likes of Babel currently hide all of this in the Node context, but is there a move to either change the standard API to async/await, or provide a standard mirror API? Either option sounds full of compatibility / maintainability headaches, so how does Node get from its current state of callback hell out the box to async / await? goroutines aren't magically turning async calls into sync, just hiding them deeper "under the hood"; there must be hope for Node.
- pitaj 10y agoThe async library has been a definitive resource for years.
- stymaar 10y agoAsync/await is a really wonderful addition to JavaScript !
- aswerty 10y agoThe Koa web framework built by the guys behind Express is pretty neat. You can do stack like calls with it, rather than callbacks, via generator functions. Not sure what the uptake is like but you can find more at http://koajs.com http://koajs.com.
- ffiw 10y agoLatest version has replaced generators with async/await.
- aswerty 10y agoGood to know! I have an app running on it but haven't touched it in a while.
- lhnz 10y agoHave they fixed the middleware graveyard? When I last used it, I found it insanely difficult to pick the correct middlewares due to the changes between versions.
- always_good 10y agoThat won't get better until koa 2.x drops the @next flag (when async/await lands in node). But I've never run into an issue that was "insanely difficult".
- GeneralMaximus 10y agoYou have a number of ways of keeping a handle on async code now. 1. Native ES6 promises will cover most of your basic needs. See https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Promise https://developer.mozilla.org/en/docs/Web/JavaScript/Referen.... In fact, unless you have very specific needs not covered by native promises, you shouldn't drop a third party library into your project. 2. For more advanced operations on promises, use Bluebird. See http://bluebirdjs.com http://bluebirdjs.com and http://bluebirdjs.com/docs/api-reference.html http://bluebirdjs.com/docs/api-reference.html. It has a ton of features built on top of native ES6 promises. If you're using an older version of Node, it also acts as a polyfill. It can also make it easy to work with libraries that only expose a callback-based interface. So yeah, Bluebird is the shit. 3. co is a generator-based control flow library that can make your async code look more like synchronous code. It's pretty cool, but I haven't personally used it in a project, nor do I know of anyone else who uses it. It builds on top of promises and generators, and isn't very hard to understand under the hood. See https://github.com/tj/co https://github.com/tj/co. I wouldn't recommend it, but it might be something to play with because ... 4. ... async/await is coming to JavaScript! At a very high level, this pair of keywords is syntax sugar for what co already does as a library. This is the final stage in the evolution of taming callback hell in JavaScript, and it builds on top of everything that has come before (promises and generators). Here's a tutorial: https://blog.risingstack.com/async-await-node-js-7-nightly/ https://blog.risingstack.com/async-await-node-js-7-nightly/
- ykler 10y agoI am confused. Your recommendations 1 and 2 seem to conflict with one another. I use Bluebird now for promisification, and I also used a few other features including the resource management (disposers). Does this mean that I will be using Bluebird and not native promises for the foreseeable future? Or can/should I use Bluebird with native promises? (Or would that just make things slower if it is even possible?)
- GeneralMaximus 10y agoAutomatic promisification and the disposer pattern are both Bluebird-specific features that native ES6 promises don't support, so you'll have to continue using Bluebird if you rely on them. However, promises returned by Promises/A+ compliant libraries -- and this includes native ES6 promises -- are interoperable with each other. That means you can mix Bluebird promises with native promises for the most part. E.g, passing an ES6 promise to Bluebird's Promise.all will work just fine. This works up to a point, and breaks when you try to use a Bluebird-specific function that's expecting additional functionality to be attached to the promise object. I don't see why mixing Bluebird promises with ES6 promises would make anything slower. You shouldn't worry about this.
- k__ 10y agoThere is also a Reactive Extensions implementation available to handle async stuff. So if you know these from another language, RxJS is worth a try :)
- madeofpalk 10y ago> lack of a definitive library to handle callback hell put me off. 1) Promises. 2) Caolan's async is wonderful for handling all sorts of 'async' problems, like async map and stuff. As close as a de facto library as you can get.
- ihsw 10y agoThe standard library is still callback hell and the async package doesn't use promises at all.
- bmpafa 10y agoPromises have been native in node for awhile, so any time you spend in callback hell is entirely your prerogative.
- ihsw 10y agoWrapping every standard library method with my own promises sounds like just another flavour of hell, and it still doesn't address the fact that the standard library is callback hell.
- sotojuan 10y agoI agree with you but FWIW there are both big (Bluebird) and small (pinky-promise) packages to promisify stuff.
- always_good 10y agoCallback hell isn't the characteristic of having a callback-based API. It's the code you get when you try to compose it and conditionally fork it without any better abstractions. So promisifying a callback-based API does address it by either letting you use a yield/await abstraction or at the very least promise composition. Wrapping a callback-based API is such a mechanical process that you can wrap entire modules at a time, so I'm not sure what's hellish about that when it gives you promises, something you can actually work with.
- norswap 10y agoMany people are complaining that the standard library still uses callbacks and so even though you have async/await, you can't use it with the standard library. I wholeheartedly agree. I don't understand why someone hasn't build a compat library that simply promisifies all the standard library (it isn't that big), taking the edge cases into account.
- rco8786 10y agoThere are like 10 of those, fwiw.
- spion 10y agoBecause the standard library doesn't really have that many callback based functions. The more important issue here is that Promises are seen as fundamentally incompatible with post-mortem debugging due to their empty-the-stack requirement: https://promisesaplus.com/#point-34 https://promisesaplus.com/#point-34 If the stack is emptied when handling an exception, the context in which that exception happened is completely lost, so its not clear how to get the process to dump a core that contains meaningful information regarding the problem. This is whats currently blocking node from fully switching to promises. Apparently many companies that have influence in node core rely heavily on post-mortem debugging and don't find the situation acceptable.
- jcheng 10y agoBoth Q and bluebird promises optionally support long stack traces, albeit at a performance hit. And in practice even old style callbacks often (usually?) empty the stack.
- spion 10y agoNo, old style callbacks don't empty the stack on thrown errors - they crash. Long stack traces are fine, but post-mortem debugging means analyzing the entire program's core dump. That includes everything that was in the heap at the time, and not just the names of the functions on the stack but also their arguments (which often point to stuff in the heap). It can be used to reproduce most of the program state at the time of the crash. The real problem is that at some point node decided that throw = programmer errors = core dump. This is wrong and misguided, but thats another story...
- lenkite 10y agoRxJs (or equivalent FRP streams library) handles this perfectly.
- zubi 10y agoSomeone already mentioned about the co library. My personal experience is that it is excellent and massively simplifies async calls. The API resembles the async/await pattern: you use the keyword "yield" instead of "await". Async calls look as if they are sync. The code that includes heavy use of async methods such as in a database application greatly benefits from co in terms of code readability.
- z3t4 10y agohttp://callbackhell.com/ http://callbackhell.com/ I think the mental gymnastics needed to write non blocking code also makes you understand the flow of your program, witch allows good abstractions. After a while it becomes a second nature and you get a mental picture of all branches. Thinking about code as events (button.onclick = showPicture), makes you a better programmer, as this is how a computer work. And when your program has to scale across several CPU's or servers, it will come naturally to you. Multi threaded solutions can be easier at first, but when you need to have threads that communicate, and handle locks etc, that too becomes hard.
- erikpukinskis 10y agoYes! Callback hell is a gift that forces you to refactor code until it's easy to understand and your abstractions are correct. Promises are a band-aid that gives you shallower indentation, and then simply hides the callback complexity in invisible objects that are even harder to debug. The problem is the modern web development world is all about creating two classes of programmer: framework programmers who control their entire stack, and application programmers who suckle at the teat and can't modify libraries, only writing composite works out of building blocks. Application programmers only feel safe when they have an exhaustive palette of libraries that they can use, because they know they can't modify anything. They thrive on feeling taken care of. Framework programmers only feel good about themselves when they know they're working on something so complicated that application programmers will never be able to understand it. They enjoy creating an simple interface for the common man, while solving "hard problems" in abstract domains. They thrive on superiority. This creates an impermeable layer that can never be refactored (framework programmers don't have access to app code, app programmers can't modify the frameworks). So when someone encounters callback hell, the necessary refactoring to find the right interfaces is impossible. Promises work well in this dead layer, because they create a clear boundary of responsibility between these two kinds of programmers. But the cost is thousands of tiny invisible state machines, with no labels or semantics.
- always_good 10y ago> Callback hell is a gift that forces you to > refactor code until it's easy to understand That doesn't sound like a gift to me. Unless you meant they are so painful as an abstraction that they gifted us with promises and then async/await. Almost everyone here has worked with callbacks and you'll be hard-pressed to find people that felt like they improved the code base. Just tiny changes to the logic, like an if-statement with branching async behavior, would cause disproportionally large changes in the callback structure, pretty much touching every line/indentation. The rest of your post is really negative and judgmental. Not sure why you felt it was necessary.
- athenot 10y agoThis is probably not the answer you're looking for, but you could look into ways that make callbacks enjoyable. The syntactic sugar provided by CoffeeScript goes a long way to make readable (and, dare I say, elegant) what would be a tangled mess in straight up JavaScript: # Get profiles LIST as JSON .post "/list", (req, res) -> db.maria.query getProfilesQuery, { userId: req.session.passport.user }, false .on "result", (result) -> rows = [] res.status(200) result .on "row", (row) -> rows.push row .on "error", (error) -> logger.warn error .on "end", -> res.json rows res.end() 5 callbacks here but they don't impede readability.
- Mahn 10y agoMaybe it's just me, but this would look infinitely more readable to me if I could tell where each callback starts and where it ends, i.e. in plain JavaScript.
- always_good 10y agoWhat impedes readability here is that you don't simply have an async function `getProfiles({ userId })` that gives you a list of profiles, either taking a callback or returning a promise. A lot of that is pointless noise. You don't need to set the status code to 200 or call end(). `res.json(profiles)` will do. And these days, Coffeescript is just Javascript with some optional parens. Can't think of many upsides in 2017 to not just using ES6.
- ausjke 10y agoTried with node.js for a few months and now back to php7/lavarel, node.js eco-system still evolves quickly and I need a stable backend right now, so far php7 works well for me and seems the development is faster.
- skynode 10y agoTypeScript async/await helps a lot with this. Type safety plus async ops. Awesomeness.
- kuzmin 10y agoI feel like it should be clarified here that async/await is not exclusive to TypeScript, but a ES7 (?) feature that is also available if you use the Babel transpiler. Having type safety is great of course, but it might be too heavy to add if you just want good async handling.
- h1d 10y agoYou may add types at a later time while enjoying the rest of the features.
- raksoras 10y agoShameless plug: https://github.com/raksoras/koroutine https://github.com/raksoras/koroutine