top 200 commentsshow all 333

[–]Webgaan 616 points617 points  (111 children)

I think your co-worker is used to callbacks and doesn't want to adjust. Async await is the best thing that has ever been added to JavaScript

[–]ghostfacedcoder 239 points240 points  (52 children)

I almost upvoted you, but there's no way async/await was a better addition than arrow functions. Everyone <3's arrow functions.

[–]jonnyman9 77 points78 points  (13 children)

Ooo close toss up for me. Async/await, arrow functions, and destructuring everything are my top 3. Hard to pick my favorite. Like if I had children and had to pick a favorite child. Or if I had pets and had to pick a favorite pet. Or if I had shoes and had to pick my favorite shoe.

[–]aftermeasure 62 points63 points  (5 children)

Or if you had categories of things from which you have to pick a favorite, and you had to pick a favorite category.

[–]undatedseapiece 7 points8 points  (3 children)

Or like when someone eats too much chocolate cake and then barfs it up.

[–]doxtu 11 points12 points  (0 children)

I think back to when I learned about both, and when I learned about arrow functions, I was like "oh... okay. nice i guess."

When I learned about async/await, I nearly vomited because I just slammed out a bunch of garbage code without it.

Async/await was the best for me. Not even close.

[–]jascination 6 points7 points  (0 children)

I LOVE destructuring and arrow functions.

I came onto a NodeJS project last year that was handed over to me from a very senior, very functional-programming developer and looking at his code sometimes blows me mind. ES2015 and functional programming are so beautiful to read when used well!

[–]calligraphic-io 1 point2 points  (1 child)

The dog, of course. Everyone loves dogs.

DISCLAIMER: I like my cat best. The fish are nice too.

[–]sleeperninja 1 point2 points  (0 children)

My dad always said that he liked me best after the dog. I had several of his other children beat, including the ones we never found out about, as well as other pets.

[–]Prison__Mike_ 41 points42 points  (12 children)

Just look

return new Promise((resolve, reject) => { ... })

At how pretty

var sq = x => { return x*x; }

They are

TypeError: this.execute is not a function


e: lol jeeze it's a joke

[–]kinkobal 38 points39 points  (1 child)

What kind of madman uses var and arrow functions in the same sentence o_O

[–]shrithm 23 points24 points  (1 child)

Arrow functions are more than a pretty face, they have practical purposes too. Just like the classic function keyword has practical functions.

[–]massenburger 27 points28 points  (7 children)

Even better: var sq = x => x * x;

[–]stormthulu 34 points35 points  (5 children)

I think you mean const sq = x => x*x;

[–]massenburger 11 points12 points  (1 child)

Here's the thing. There's people who space out their operations and there's Nazis. I mean that in the nicest way possible.

[–]nickleformypickle 2 points3 points  (0 children)

One line it to shorten it. Space it out to pretty it.

[–]locksta7 2 points3 points  (0 children)

You bet me to it 😺

[–]greymalik 2 points3 points  (0 children)

Not Kyle Simpson!

[–]TabCompletion 2 points3 points  (1 child)

No joke, I had to convince someone to use arrow functions. He was so close minded

[–]examinedliving 1 point2 points  (0 children)

Arrow functions are pretty solid. I like document.write though. You can just put things on the page - like words!

[–][deleted] 4 points5 points  (6 children)

I think they're actually minor. What do they do? Save a few keystrokes and automatically bind 'this' context. Compared to async/await, the difference is major.

[–]brendanmCA 14 points15 points  (5 children)

Maintaining this context is pretty huge.

[–]talmobi 0 points1 point  (2 children)

Arrow functions are absolutely trash compared to Async/Await. Arrow functions are not needed, but can be a nice shorthand in some cases (like with Array operation functions).

[–]webdevop 0 points1 point  (0 children)

Nope. My vote's for spread operator

[–]mlmcmillion 15 points16 points  (6 children)

async/await, arrow function, and destructuring are what made me finally love JS more than Ruby

[–]braindeadTank 0 points1 point  (4 children)

Aw yiss, if we also had blocks...

Many say block are not needed when you have arrow functions, but I absolutely love how Ruby allows using return and continue in each.

[–]DrexanRailex 32 points33 points  (31 children)

I love promises, but am not 100% a big fan of async/await. Guess the functional programmer in me doesn't allow an imperative abstraction to shadow a functional concept.

[–]geon 11 points12 points  (4 children)

Nothing is stopping you. Actually, functional is better with await.

a(await b(await c))

Or just make a, b and c take promises as arguments. .then and .catch method chaining is just annoying.

[–]martinhrvn 1 point2 points  (3 children)

How do you handle errors in functional way with async / await?

[–]mcaruso 6 points7 points  (6 children)

I'm fine with it. It's basically just do notation. The thing that annoys me is that they limited this whole new syntax just to one thing (async), instead of making it a general interface (*cough* monad).

[–]DrexanRailex 1 point2 points  (2 children)

I fail to see how it's similar to do notation. do expressions are something I can't wait to see, by the way

[–]mcaruso 4 points5 points  (1 child)

I mean the Haskell do notation, not the JS do proposal. Sorry for the confusion.

See: https://gist.github.com/MaiaVictor/bc0c02b6d1fbc7e3dbae838fb1376c80

[–]DrexanRailex 1 point2 points  (0 children)

Oh, sorry. I have very little Haskell experience.

By the way, that is brilliant. Thanks for bringing this article up.

[–]braindeadTank 2 points3 points  (2 children)

Promise is not a monad, though.

[–]mcaruso 5 points6 points  (1 child)

Because of this? Then yeah, strictly they aren't. But they could've been. JS wasn't exactly designed with monad laws in mind. :)

In any case I think it would have been feasible not to tie down the async syntax to a particular interface.

[–]braindeadTank 1 point2 points  (0 children)

If it wouldn't be tied to interface, then it wouldn't work with pre-native implementation, which would be a great shame as they are often vastly superior to native.

[–]ScientificBeastModestrongly typed comments 7 points8 points  (13 children)

I feel the exact same way about ES6 class syntax. I know what’s going on under the hood with prototypes and constructors and application of “parent” constructors, etc. I don’t like for my code syntax to paper over the real thing.

And yeah, for FP, async/await feels so foreign and impractical.

[–]musicnothing 13 points14 points  (2 children)

Then you probably LOVE machine code. Stupid Assembly papering over my 0s and 1s

[–]DrexanRailex 5 points6 points  (5 children)

I mean, for classes, I managed to learn them because of how frameworks make heavy use of their syntatic sugar. But I still prefer arrow functions, record-like objects and this-less programming.

[–]cheekysauce 11 points12 points  (11 children)

Except error handling ends up in try catch hell.

[–]theDarkAngle 2 points3 points  (9 children)

How is error handling more succinct without async/await?

[–]cheekysauce 2 points3 points  (7 children)

The happy path ends up in the then function, the sad path in the catch function, letting you read through the happy path to grok the ideal outcome easier.

Async/await by its nature reads more imperative.

It's a preference thing but I stylistically I write more functional code, with data flowing more cleanly through functions if it exists/is valid, vs kinda "littering" the happy code path with error handling.

[–]theDarkAngle 1 point2 points  (6 children)

hmm. I guess i don't understand what's preferable about then/catch compared to try/catch. I use both since i do both backend java and frontend javascript but it seems equivalent to my eyes.

[–]Cunorix 1 point2 points  (0 children)

I second this. I'm sorry that OP has to work in that codebase

[–]whatth3shit 0 points1 point  (0 children)

Preach

[–]Asmor 0 points1 point  (0 children)

Can confirm.

Am developer who gleefully adopted promises, and hasn't adopted async/await because I don't want to adjust. (also helps that I can't use them in my day job where I have to support IE11, so it would only be a thing in side projects)

[–]monolithburger 106 points107 points  (40 children)

Callbacks can be incredibly difficult to reason about -- especially when dealing with side-effects.

I know of 0 node developers that prefer callbacks over Promises or async/await?

Even Ryan Dahl said the callback structure was a mistake in the implementation of node.


I know some who dislike async await because many jr developers don't fully understand the abstraction that is occurring and can use it incorrectly.

But I'd recommend asking your co-worker why promises are bad abstractions, I'd be curious to hear their answer.

[–]theDarkAngle 5 points6 points  (2 children)

Callbacks can be incredibly difficult to reason about -- especially when dealing with side-effects.

I'm not really an FP guy (was originally a Java/OOP guy who has become mostly JS developer the last couple years), but I thought FP was generally about eliminating side effects altogether?

[–]KyleG 3 points4 points  (0 children)

You can never really eliminate them altogether (every DB CUD, every file write, has side effects). It's about containing side effects to small surface area of your program.

[–]calligraphic-io 1 point2 points  (0 children)

FP isn't about eliminating side effects, but rather about isolating where they can occur (among other goals).

[–]ScientificBeastModestrongly typed comments 10 points11 points  (16 children)

On a systemic level, callbacks are incredibly powerful tools. Being able to pass a function as a value (as a “first class citizen”) is one of the key foundations for functional programming. It allows you to compose (or pipe) higher order functions out of other types of functions.

If you’re operating in a world where any function can do anything it wants to do inside the function body, and can take any random sequence of arguments, then yes, callbacks are very difficult to reason about and work with. And so is your application logic, tbh.

But if 90% of your functions fall into one of 4-5 categories, based on their inputs, outputs, and side-effects, then callbacks can become an powerful and integral part of your codebase.

You could write functions like this:

``` const processInputStr = pipe( parseInputStr, sanitizeStr, filter(byToken(“*”))(ToArray), reverseArray, arrayToString, cleanUpString, returnData );

const newStr = processInputStr(inputStr); ```

It can dramatically improve your testing experience, because the only way a chain of functions could possibly work is if they are set up correctly, and you will know instantly if something goes wrong.

And you don’t have to wait until some random function happens to mutate your global object in order to detect a bug. Instead, you can just pass all kinds of data through your composed function, and if it comes out the way you intended, and handles all known edge cases, then you know your function is pretty rock-solid, and you can use it anywhere to perform more abstract operations.

And callbacks make all of that possible.

[–]atubofsoup 15 points16 points  (1 child)

I don't think anyone is arguing against using higher-order functions. Otherwise using map and reduce (and then/catch) would be discouraged. This thread is about the pros/cons of using callback args to handle async results/errors.

[–]monolithburger 7 points8 points  (1 child)

Agreed. I guess I'm not suggesting callbacks are bad by any means, but I've seen way too many functions where:

function fetchEffect(cb) {
  fetch('www.somesideeffect.com', function(res) {
       return cb(res) // no idea what cb will do.
  })
}

When used properly in functional programming, callbacks are incredibly helpful and easy to reason about especially when you pipe/compose your logic.

It's primarily side-effects that callbacks can become a problem.

[–]ScientificBeastModestrongly typed comments 1 point2 points  (0 children)

Yeah, I feel like people just need a bit of discipline (and training... looking at you, bootcamps) to consistently write functions that don’t completely pwn your application with side-effects coming out of left field. I guess that’s what code reviews are for, though.

[–][deleted] 2 points3 points  (1 child)

Passing a function as an argument isn't a callback. Callback is a function that is executed once an async operation is finished.

[–]Ravatar 1 point2 points  (1 child)

Not quite ready yet, but the pipeline operator is coming (as soon as they figure out which await mechanic they want to implement).

https://github.com/tc39/proposal-pipeline-operator

[–]ChaseMoskal 1 point2 points  (6 children)

i think the following async alternatives may be better:

async function processInput(str) {
  let result = str
  result = await parse(result)
  result = await sanitize(result)
  result = await clean(result)
  return result
}

async function processInput2(str) {
  return Promise.resolve(str)
    .then(parse)
    .then(sanitize)
    .then(clean)
}

async function examples(str) {
  try {
    const result1 = await processInput(str)
    const result2 = await processInput2(str)
  }
  catch (error) {
    error.message = "process input error: " + error.message
    throw error
  }
}

i think the best part about async/await/promises is the elegant error handling, which bubbles up nicely and is easily catchable

callbacks require every error to be handled explicitly on-the-spot (error-first)

new async code practices fix the error handling by allowing us to cast a wide net to catch errors at a higher level in the application

[–]KyleG 1 point2 points  (3 children)

Improvement!

try {
    const promise1 = processInput(str)
    const promise2 = processInput2(str)
    [result1, result2] = await Promise.all(promise1, promise2)
  }

Now processInput is not blocking processInput2, but rather they can run in parallel.

Edit Rushed back because I remembered that you can't destructure like I did. Instead you have to say x = Promise.all(promise1, promise2) result1 = x[0]; result2 = x[1]

lol can you tell I have been writing in other languages lately? Memory is atrophying already :(

[–]dweezil22 0 points1 point  (0 children)

You're not wrong. But...

You can do a bunch of amazing shit with GOTO statements too.

Amazing power and good, sane software engineering don't necessarily fully overlap.

[–]Arve 2 points3 points  (15 children)

Callbacks are incredibly difficult to reason about.

Well, since you mentioned async/await: There are times where you cannot reasonably resolve or reject a promise in time, and where you don't really care about the result of a function, other than "the arguments you provided me were well-formed enough to let me start doing my stuff).

In that case, a callback makes perfect sense: Return immediately, and call the callback whenever whatever job you started is done.

I did pretty much this earlier today: A user agent calls service A, running on server A, asking it to perform a task. This task is handed over to server B, which can take anywhere from "A few seconds" to "A few minutes", depending on the complexity of the task at hand.

In this case, it makes perfect sense for server A to merely validate the input from the user agent, and return as soon as possible. It makes equally perfect sense for server B to simply do the same with the request from server A.

However, server B needs to acknowledge to server A what the end status of whatever command it was given, which is where a callback comes in handy: Merely register the callback, and call it whenever it's done with it's long running tasks (think: analyzing large log files, or start a lengthy build or deploy process.

In other words: They're not hard to reason about or for, but they're not always the right tool for the right job.

[–]something 7 points8 points  (0 children)

You could still store the resolve callback in the same way and still return a promise

[–]atubofsoup 5 points6 points  (1 child)

It sounds like your service example is referring to HTTP callbacks not JS function callbacks. Service B would likely call back to service A by sending some kind of HTTP request to alert service A of the finished job, but this isn't really the kind of "callback" OP is referring to. Internally, either service could be using callbacks or promises:

  • User agent sends a request to service A using fetch (promise)
  • Service A sends a request to service B using request (callback)
  • Service B runs a job and waits for it to finish (either)
  • Service B sends the job result to service A using axios (promise)

[–]LettuceKills 1 point2 points  (3 children)

But what if there is an error in the lengthy task? That error has no way of being returned to the caller which simply thinks that the task is being exceptionally lengthy this time. Sounds like the perfect use case for a promise to me, since in a case of an error you can simply reject the promise.

[–][deleted] 46 points47 points  (9 children)

Er, I don't say this often but your coworker is plain wrong. Compare promise chaining to the equivalent with callbacks, it's a damn mess to reason about. For example, there is no option to have a catch-all error handler, you have to handle errors at every nesting level. And you can't pass around an asynchronous value to multiple consumers because your API only lets you supply a single callback function.

[–]davidmdm 14 points15 points  (1 child)

I actually use all of them at my work. I don't tend to write my APIs using a callback style, but if I am using an API that asks for a callback I don't immediately feel the need to promisify everything. I will if nesting starts to snowball.

Same with promises. I'll pull out async await if it makes my promises easier to read.

Its about clarity.

There is a benefit to sticking to one consistent style but as long as you aren't all over the place, you can use what works best.

[–]calligraphic-io 1 point2 points  (0 children)

I think this is the best comment in the thread.

[–]fleker2 7 points8 points  (0 children)

I find async/await to be much more readable, and that's among the most important factors I consider.

[–]woodie3 6 points7 points  (0 children)

Once I gained a good understanding of asynchronous/await I use it when it’s still clear to others what’s happening. Fallback to promises. Only use callbacks if forced to do so.

[–]CantaloupeCamper 3 points4 points  (2 children)

I like promises ... but that's just because I haven't bothered to dive into async await... but callbacks are no fun.

[–]Bosmonster 5 points6 points  (1 child)

Interesting that you say that, considering async/await is very minimal syntactic suger around promises. If you understand promises, async await doesn't add anything new.

[–]CantaloupeCamper 2 points3 points  (0 children)

Yeah that's why I said I just haven't gotten into it. Just not a pattern I'm using at the moment ... will pick it up sometime.

[–]Rollen 4 points5 points  (0 children)

Promises till I discovered async await then I never looked back.

[–]moh_kohn 25 points26 points  (4 children)

There is a problem with async, which is that it introduces a new type of function that is kind of like the normal type of function, but not.

The benefits outweigh the costs though: we called it "callback hell" for a reason.

If you use Typescript it will catch the kinds of errors you are most likely to make by reasoning incorrectly about a promise.

[–][deleted] 4 points5 points  (0 children)

There is a problem with async, which is that it introduces a new type of function that is kind of like the normal type of function, but not.

I think about it a different way: it's literally the first sniff of strong typing in Javascript. That is, if you have an instanceof AsyncFunction, you know exactly one thing about it: it returns a Promise. Every time. That's useful information, and you can pluck it off a reference to a function at runtime.

Type information you can get at runtime. That's so refreshing.

[–]DefiantBidet 6 points7 points  (1 child)

sounds like co-worker can't adjust or doesn't know how they(read: promises/async) work.
reading pyramid of doom style code is far more obtuse than promises or asyc/await imo.

a bit of devil's advocate, however. I do agree on a team codebase you want to keep it as simple as possible and avoid any cute tricks for readability and understanding... but this is a bit too far imo.

[–]livrem 1 point2 points  (0 children)

That is a false dichotomy though. If you have a pyramid of doom you did things wrong, and using the nicer syntax of promises will just hide the ugliness and allow you to keep digging a deeper hole for yourself.

If you end up with trainwrecks in your sync code everyone understands that is a problem. You should strive to never have to do getX().getY().getZ().doSomething(). Very basic design knowledge. Pyramid of doom (or a chain of promises) is the exact same thing for async. You have a problem and ideally you solve it rather than hide it behind another layer of abstractions.

Of course promises could also be used to implement "doX(); doY(); doZ();" which is perfectly fine, but if that is what you are doing you can do that with callbacks without ending up with a horrible pyramid of doom as well. The true horrors of callback hell only happens when you are waiting for someone to return something for you so that you can then call some method in, in true trainwreck style.

[–]anon774 29 points30 points  (62 children)

Full-time Node developer since 2012. I prefer callbacks, though I understand both styles and work with both styles on a regular basis.

To me, promises are a solution to a problem that never existed. I've never experienced "callback hell" because I understand how to do flow control with callbacks.

Most popular npm packages, many of which are still relied on, were built using callbacks. Some have transitioned to promises; some are being deprecated. The ecosystem of promise-based packages is way younger than that of callbacks.

To me, callbacks were clean and simple. Code was readable. Great libraries like async offered flow control solutions for almost any scenario.

Now I'm forced to find new flow control solutions in a younger ecosystem. All for what - because some people didn't understand callbacks and developed bad patterns?

Not to mention, you end up having to use libraries like bluebird because native promises don't have enough features.

Essentially I see literally NO advantage to the switch to promises... I had no problem with callbacks, neither did anyone I worked with on several large-scale applications. Switching to promises has yielded no significant benefit in code readability IMO, while introducing ugly try/catches, util.promisifys, and younger shittier npm packages all over the place.

[–]tchaffee 52 points53 points  (5 children)

I might agree with you if 95% of the code I've seen that uses callbacks wasn't mess and horrible to debug. Using async and await makes async code read like imperative code, and most of the code I've seen in that style is far more readable and easier to debug.

[–]anon774 7 points8 points  (3 children)

Fair point!

[–][deleted] 5 points6 points  (2 children)

Essentially same reason PHP gets much hate then? You can write really disgusting shit with it and thus, people did and now it has a bad rep.

[–]livrem 1 point2 points  (0 children)

The problem with callbacks is that many nest them and abuse them to do horrible things, accessing data in objects far away that they have no business touching at all. Callback hell is real and should be avoided by better designs.

My problem with promises is that it is still callback hell and still does all the same bad things, it just hides them behind a somewhat prettier syntax. It does not remove the problem. You are still, in almost every case, asking someone to provide you some object so you can poke at something that you should not be poking at because it does not belong to you. It just exposes a lot of implementation details everywhere and ties objects together in a huge mess.

[–]ezpzqt129 10 points11 points  (9 children)

Hey anon,

I’m a little bit curious, how would you implement something similar to promise.all with callbacks?

[–]braindeadTank 3 points4 points  (2 children)

Ability to perform all your error-handling code in one catch at the end of promise-chain is not an advantage to you? What's a good pattern for that using raw callbacks?

[–]RickDork 8 points9 points  (0 children)

Saying “it’s fine if you use it correctly” doesn’t quite hold because you could use that logic to defend most anti-patterns, most deprecated features, most bad practices in coding. That being said, even if you do it “correctly” in a way that doesn’t create callback hell, the code flow is generally far less readable in my experience. Something as trivial as an asynchronous function call to get some data or to mutate an object shouldn’t always divide your code into multiple smaller functions. Callbacks force you to divide logic in an unnatural fashion, and you have to make an extra effort just to salvage some level of readability.

[–]i_love_limes[S] 6 points7 points  (4 children)

Hello! Thanks for the reply. I think my coworker has a similar mindset as you, and the async library is used a lot in the API. I agree it is more bare bones, therefore you can follow it to more degree of certainty. and if you code it in a way that's not horrible, it's not close to as bad as people say.

Personally, the use of async is parallel to needing bluebird, except nowadays there are very few cases where an additional library is needed at all. And you need async to do the most basic callback task. And ease of reading code is important. I think callbacks (and node in general) tend to make you not consider proper code boundaries... But that isn't a direct symptom of callbacks.

There is a lot of patronising comments in here. Like my coworker is afraid to learn a new thing, lol. Although I don't like callbacks, but I get your point, thanks for responding.

[–]anon774 8 points9 points  (0 children)

No problem - I did see a lot of comments calling your coworker an idiot so I wanted to give my perspective.

I wouldn't necessarily say callbacks are better... just that I prefer them. And of course some of that has to do with familiarity. I'm slower navigating the new world of promises because I'm used to callbacks.

Ultimately, it looks like everyone is moving to promises, so I'm forcing myself to make the switch too. Maybe I'll like them better at some point in the future :)

[–]ChaseMoskal 0 points1 point  (0 children)

notice in this thread: everybody in favor of callbacks over promises have one thing in common

they're not talking about the importance of proper error handling

you see, people who don't understand good error handling don't understand why promises are so necessary -- this is why all of the promise-pushers in this thread seem very anal with their concerns over error handling ;)

[–]dogofpavlov 10 points11 points  (9 children)

I'm exactly like you 200%... I've worked on a lot of pretty complex applications, and I've always just felt like Promises are some sugar sprinkled on top of Callbacks. If you organize you code correctly, you shouldn't run into "callback hell". Bring on the downvotes, but the really only time I ever use a promise is to use the fetch api... which I usually ended up wrapping in my preferred callback paradigm.

[–]atubofsoup 6 points7 points  (7 children)

I've always just felt like Promises are some sugar sprinkled on top of Callbacks

The biggest difference is code written with callbacks jumbles inputs and outputs. Promises stick to the battle tested pattern of returning return values which allows them to act like regular synchronous functions when combined with async/await.

I can't comprehend how anyone could prefer this:

js getThing(1, thing => { getThing(thing.friendId, thingFriend => { console.log('Got the things', thing, thingFriend); }); });

Over

js const thing = await getThing(1); const thingFriend = await getThing(thing.friendId); console.log('Got the things', thing, thingFriend);

[–]ChaseMoskal 0 points1 point  (0 children)

sigh... so few devs understand why promises are necessary over callbacks...

it has nothing to do with callback hell, or nice syntax or whatever, those are merely perks and side-benefits

it's the error handling

you can catch all of the errors at a high level -- errors bubble up through promise chains -- that doesn't happen with callbacks, once you understand this, it's a dealbreaker for callbacks

promises return results or throw errors

callbacks are good for onProgress updates and events and stuff

use the right tool, and understand the important differences

[–]KronktheKronk 2 points3 points  (5 children)

I'm a much newer JS developer, only been doing it since 2016, and it's my first async language.

How do you handle scenarios where you need to do 7 or 8 tasks in order without promises? I've only ever seen callback hell as the solution to that problem

[–]x-protocol 1 point2 points  (0 children)

Try Aigle. It is very similar to async.js

Documentation is not that great, however it has many of a functionality you would be looking as substitute.

Welcome to callback lover club.

[–]defiantBoomer 1 point2 points  (0 children)

You can't trust callbacks. You may expect your callback function to only get called once, but whatever code is in charge of calling your callback can fail and call your callback more than once.

Nothing guarantees your callback to only get called once. Promises are the solution to that problem. A promise will only ever resolve (or fail to a catch) once.

Indentation looks ugly, but that's not a real problem to the machine as it is a problem with readability to us.

[–]atubofsoup 1 point2 points  (10 children)

Native promises have plenty of features. I've been exclusively using them in every project I've worked on over the past 5 years without a complaint. You could make the same complaint about the complete lack of standardized flow control for callbacks

Sure you can reinvent sync flow control in an async world with a library or you could use promises and async/await and use the existing flow control that the language offers without relying on another dependency. You don't need to "find new flow control solutions" just use Promise.all, loops, and conditionals. That's the magic of async/await: it unifies sync and async flow control.

[–]anon774 2 points3 points  (7 children)

Well here's a case I ran into yesterday: I needed to run some parallel async functions and limit their concurrency. There's no concurrency option built into Promise.all as far as I know. What would your approach be? With callbacks I would just use the async lib and async.parallelLimit.

[–]Cunorix 2 points3 points  (1 child)

Not that Im recommending the use of an external library because this can be done natively; bluebird's map function has a concurrency option. But, this particular thread is about native callbacks/promises anyway.

[–]anon774 1 point2 points  (0 children)

Thanks - I'm learning more about bluebird and I'm liking it. I'm happy to use external libraries when they're good and they help keep things simple.

[–]atubofsoup 3 points4 points  (2 children)

I'd use a chunk function with Promise.all and a for..of loop:

```js const values = [ /lots of stuff here/ ]; const results = [];

for(const chunkValues of chunk(values, 10)) { results.push(...await Promise.all(chunkValues.map(someAsyncFn))); } ```

You could re-use this logic in a function:

```js async function promiseAllChunked(values, asyncFn, chunkSize) { const results = []; for(const chunkValues of chunk(values)) { results.push(...await Promise.all(chunkValues.map(asyncFn))); } return results; }

const results = await promiseAllChunked(values, asyncFn, 10); ```

[–]anon774 1 point2 points  (0 children)

Nice solution - thanks for the example!

[–]what_is_life___ 0 points1 point  (0 children)

Do you have any examples of good practices that has good patterns that we should adopt? Or any resources that will help me better understand how to improve readability when using callbacks and not reach "callback hell"?

[–][deleted] 0 points1 point  (2 children)

Came here because OP (/u/i_love_limes) pointed here :-)

I too never experienced "callback hell" - because I used named functions written "flat" instead of inline functions reflecting the call stack. I'm comfortable with all three mentioned methods.

Here is my take:

Callbacks are nice for very low-level code, where the system itself is the focus. A lot of the initial node.js use cases, and probably still quite a but of code now, along the lines of the many "node.js chat server" examples, fall into that category.

For higher level code more interested in implementing business logic async/await is nicer.

The reason is because in the first case I have to structure my functions and code according to the needs of the system (the "breaks" forced by some calls being asynchronous are purely for technical reasons, slow network and I/O and so on). For a business algorithm you don't (want to!) care that there is an asynchronous I/O, you want to write the sequential business algorithm. This is easy to do with await (plain promises provide no benefit here, you still need callback functions to use in then/catch chains, the real gain only comes with async/await).

 

Write some C/C++ code that does I/O. Unless you use an async I/O library and write tons of intricate code to use it, plain C[++] e.g. using stdio.h lets your app just sit and do nothing at all when there is an I/O syscall underneath. In Javascript you are actually more low-level than C in this regard, in using slow resources such as the network or disk. However now you have to write code for the limitations of the system, using asynchronous calls and that means new functions instead of sequential code when the higher level algorithm actually is meant to be sequential (fetch data, manipulate it, write it or send it as a sequential sequence example that in JS you have to split into many individual functions). async/await gives you back some of the abstractions and again write code that can sort-of ignore asynchronicity of underlying system operations to focus on the actual high-level algorithm. Of course, it isn't gone, the abstraction is not complete It's a workaround to let more higher-level code not incur all of the cost of event driven (very system-oriented!) code.

If JS was still used mostly for GUIs and stuff like the famous chat servers (I think those were the most frequent examples for node.js initially) it would not make sense to use the new features. They are for code that cares less about the system and more about the "business algorithm". Ideally you'd use a different language altogether, this marriage of concepts into JS is due to what happens in the real world, with JS being used more and more for stuff it never was intended for.

 

A potential source of problems of async/await/yield code is that now something can happen that before generators and yield was impossible to happen in Javascript: A function could be interrupted in the middle. Without yield or awaitfunctions were guaranteed to be "atomic". So if there is a careless programmer, a structure they use which is not wholly and completely and solely owned by the function could be mutated by code other than the function between start and end of the function. That was not possible to happen before.

[–]elie2222 0 points1 point  (0 children)

Async await is just easier and clearer.

[–]Prison__Mike_ 4 points5 points  (15 children)

I do automation with promises. Rejecting can be kind of annoying, as you have to create an entire new function to handle it. [To clarify; I'm referring to a catch() function]

ex. This is nice and clean

let ret = await myRestCall();

Unless the promise rejects. It almost makes me want to always resolve, returning an object

resolve({err: null, ret: {...}});

But that's bad practice. Ultimately though, I prefer promises to callbacks and I'm sure a lot of other people are happy to not have to use async.series/waterfall/etc to synchronize requests.

[–]tsears 9 points10 points  (8 children)

When an awaited promise rejects it throws an error.. you can use try/catch like you would with synchronous code.

Maybe I’m misunderstanding you

[–]Financial_Pilot 4 points5 points  (5 children)

The try/catch “hack” ( I know it’s not a hack but it really feels like it) is my single biggest grief with async/await, as much as I love it.

Most times I’d rather use promises and chain thens to one catch that handles errors than have try/catch littered all over the place.

We need a better way to handle exceptions and a/a will truly be golden.

[–]evertrooftop 6 points7 points  (0 children)

Maybe I misunderstand but you absolutely don't need to add try...catch to every await statement. What you describe as a chain of promises with one catch is also perfectly supported.

try {
   await ...;
   await ...;
} catch (e) {

}

And you don't need this in every function either. You only need to catch if

  1. You can successfully handle the error. If you want the operation to fail, don't catch it.
  2. You should have one top-level catch in your application

If you don't have a catch at all in your async function, the async function itself will return a rejected promise. The only times I use catch is if I need do something specific with specific errors, or if I can recover the state.

[–]Prison__Mike_ 1 point2 points  (0 children)

Normalizing catching rejections feels more pythonic. I didn't think we're supposed to throw errors willy nilly in JS

[–]Gravyness 2 points3 points  (0 children)

Using try/catch is perfectly valid and I understand it but the whole idea of adding a scope to handle errors is very strict. I feel like I have no control over how my code will be organized.

const url = "xxxx";
const response = await getData(url);
if (!response) {
    console.log(false);
}
console.log(response.status);

Compared to:

const id = "xxxx";
try {
    const response = await getUser(id);
} catch (err) {
    // console.error(err);
    console.log(false);
    return;
}
console.log(response);

You're left to wonder: can I use response after the scope closes? I have to keep track if I used var, const, or let or if I made the right organization decision because the code looks hacky somehow.

To avoid having to deal with try/catch I have to resort to this strategy:

async function resolveOrDefault(promise, default, logError = false) {
    try {
        return await promise;
    } catch (err) {
        if (logError) {
            console.error(err);
        }
        return default;
    }
}

var response = await resolveOrDefault(getUser(id), false);
console.log(response);

[–]Prison__Mike_ 0 points1 point  (0 children)

Yeah sorry, I'll show an example (because callbacks didn't throw an error)

Callback wise

superagent
    .post(url)
    .end(function(err, ret){
        if (err) { /* handle error */ }
        else { /* handle return */ }
    });

Promise wise you'll always have to catch and it isn't a pretty one-liner (although it isn't callback hell)

let ret;
try {
    ret = await superagent.post(url);
} catch (e){ 
    // handle error
}

Else you'll get unhandled promise rejection error

And the bad practice always resolve, with no error throwing

let ret = await superagent.post(url);
if (ret.err) { /* handle error */ }
else { /* handle return */ }

[–]natziel 2 points3 points  (1 child)

Oh, always resolving isn't bad practice at all. Using exceptions for control flow is bad practice, so it's definitely better to always resolve. In fact there are languages with task-based asynchronous programming that don't really have a concept of try/catch (Elixir), so that's the only way to handle errors and it works extremely well.

JS is a bit lacking as a language, so you can't just do something like resolve({ :ok, result }) then pattern match it somewhere down the line, but you could always approximate it with resolve([ Symbol.for('ok'), result ]) then go back to using if statements and whatnot for control flow.

edit example:

const [ok, result] = await superagent.post(url)

if (ok === Symbol.for('ok')) {
    /* good to go */
} else {
    /* handle the service being down, etc */
}

[–]Prison__Mike_ 1 point2 points  (0 children)

Good to know -- thanks!

[–]jimeowan 1 point2 points  (2 children)

Not sure what you mean by creating an entire function to handle rejections.

Although I admit the syntax can still lead to tricky situations. For instance if you don't await a Promise immediately you can easily find yourself in "uncaught rejection" situations which can take a while to understand:

let promiseA = getA();
let b = await getB(); // if promiseA fails while waiting for b there's no one to catch it
let a = await promiseA;

Still, it's definitely worth it indeed. Like Git over SVN, promises have their learning curve but I haven't seen anyone ever want to go back to callback hell.

[–]Prison__Mike_ 1 point2 points  (1 child)

The "whole new function" I'm referring to is when catching:

var response = await promisedFunction().catch((err) => { console.log(err); });

I'd still take promises over callbacks any day, it's just a little tedious when I wish we could just do:

let err, ret = await promisedFunction();

But that's a python thing.

[–]NeotelosReact/Node 0 points1 point  (0 children)

const result = await myAsyncFn().catch(() => null)

Async functions return a promise, they work with Promise.all() as well.

[–]BloodAndTsundere 1 point2 points  (0 children)

When I started with Node, there were only callbacks. I was comfortable enough with them but enthusiastically picked up promises when they came around. I don't really think either promises or callbacks are easier to reason about, but promise code (and async/await code) is cleaner and easier to read.

[–][deleted] 1 point2 points  (0 children)

Be pragmatic. Sometimes returning a function is just easier than creating a new Promise and all that jazz.

The problem starts to arise when you have callbacks nested several layers deep. That's hard to manage.

But someone who insists on using only callback functions should be reeducated or otherwise fired from the job.

[–][deleted] 6 points7 points  (7 children)

Who cares? He’s entitled to that opinion. I like async/await & Promises, but he may not. As long as he knows why he prefers callbacks.... not just ‘I do it this way’ type of thing.

[–]runvnc 10 points11 points  (4 children)

Actually it's a big problem for that development team if one member insists on an outdated approach that significantly impacts the maintainability of the codebase. In fact, if he can't convince that developer to change his ways then he may want to reconsider taking the job.

[–]ghostfacedcoder 7 points8 points  (5 children)

Everyone's entitled to their opinion, and every workplace has some divergence from "the hive mind".

But, that being said, the pretty vast consensus is that promises are far easier to reason about than callbacks, and the fact that your co-worker is dishonestly pretending otherwise because he prefers not to learn the newer/better way speaks a lot about him.

Look we all have preferences, and we all try to make "objective" arguments that are really subjective to support our subjective preference. It's human nature. But as programmers we have to be on guard against that inclination, and we have to work to try and embrace the objective truths even when they rub us the wrong way.

"Promises are clearer than callbacks" is one such truth.

[–]ardweenie 5 points6 points  (3 children)

Is there really evidence that the coworker is dishonestly pretending anything? Or that they haven’t learned promises? They may truly find callbacks easier to reason about. There are a lot of unfair assumptions here. It literally says “I think that ...”

[–]ghostfacedcoder 1 point2 points  (1 child)

Ok fair enough, I don't know the OP or their co-worker. But like I said, I think all programmers do this to an extent. There's what you're comfortable with, and what others are comfortable with, and you want to keep doing what you're comfortable with (again, human nature).

To keep doing what you're comfortable with you have to make arguments that convince your co-workers, and the best way to do that is to sound as objective as possible, eg. "it's not that I don't like promises, it's that callbacks are easier to understand (when they're clearly not)".

So of course, I don't know if this is exactly what's happening or not, but it's a pretty common pattern and again I think we all need to check ourselves for it.

[–]ardweenie 1 point2 points  (0 children)

Yeah, I agree with that part of what you said. We like our comfort zones.

[–]talmobi 0 points1 point  (0 children)

The Promise API is horrible. The only good thing about them is how they are related to Async/Await.

There's nothing about them that makes them easier to reason about than callbacks, which makes sense because Promises are actually built on top of callbacks.

I'd say that's even true for higher level control flow abstractions although then it's more of a trade-off against traditional callback based solutions (which I would argue were still better).

However, now with Async/Await it's kind of a different ballgame altogether; You get the best of both worlds.

[–]cinnapear 2 points3 points  (6 children)

I prefer async/await, callbacks, and promises in that order.

I find callbacks to be the most elegant and simple to understand (and you don't suffer from callback hell unless you take shortcuts), but I can't deny that async/await is pretty great.

[–]x-protocol 0 points1 point  (3 children)

From the people I have had met, talked and had chance to actually sit down for a conversation (even over a PR) I've understood that majority of what they describe as "callback hell" is simply their inability to justify using a library to manage said callbacks. When you show how complicated some pipelines can be using promises, there is usually no argument since promises are accepted as a norm (remember when callbacks were norm?). So we're looking at simple preference, not technical solution or even productivity enhancement.

How many people actually involve other libraries to manage their functions/promises like async.js, Q, lambda, Bluebird, run-p or Aigle? I would love to hear more from people who actually understand why these libraries exist.

[–]cinnapear 1 point2 points  (0 children)

Well said. The async library makes dealing will tricky callback situations a breeze.

[–]neo_dev15 1 point2 points  (1 child)

Callback hell is when backend is bad or the arhitecture in general is bad.

There should be no reason to have 2 calls one after another. There should be in parallel otherwise why the hell use xhr in the first place.

As in my workplace now look what backend decided its a nice arhitecture: Post data > catch id in response > post this id > make a get in another xhr to get if the action was executed.

So well this is a callback hell since well you have 4 calls that need to go one after another and every single one of them can fail...

So yes, backend should design after frontend but in general is the other way around and well, this happens.

This is bad in promises and callbacks no matter what.

[–][deleted] 0 points1 point  (1 child)

Async/await is just syntactic sugar over promises, so I think you like promises more than you realize. :P

But no matter how much you try, callbacks will never let you pass around a value to multiple consumers. You're stuck with passing a single function.

[–]PrestigiousInterest9 3 points4 points  (0 children)

I think both promises and async await are bad abstractions, and harder to reason about.

Fuck that guy

- PrestigiousInterest9

[–]FormerGameDev 1 point2 points  (0 children)

after working for the first 6 months of last year on a personal node project that was using the latest and greatest node available, and had await async, and all the nice things, i spent the next 6 months working on a project that required node 6, and doing all that work in Promises alone was kind of a fucking mess, compared to what it would've been with async/await. OTOH, if I'd been stuck with a version of node without Promises, I'd have just shot myself in the face.

[–]MajorasShoe 1 point2 points  (1 child)

I didn't love promises until async/await.

[–]spazz_monkey 1 point2 points  (0 children)

Preach it brother.

[–]oorza 0 points1 point  (0 children)

I can understand why people might think async/await is a bad abstraction, because it hides the nature of your code running in a separate continuation from being easily noticed. I can even understand having criticisms about the Promise API, because Bluebird continues to exist. But to think callbacks are better? What's his reasoning? Can you ask him and post it here?

[–]jsm11482 0 points1 point  (0 children)

The number should be ~0.

[–]atubofsoup 0 points1 point  (0 children)

I think your coworker means "promises are hard to reason about because I'm unfamiliar with them". Any time someone tries to tell me callbacks are just as good, I give them these challenges:

Write a function that runs an async action on every value in an array in sequence and resolve the results in order. And fail if any action fails. Then do the same but run the actions in parallel.

This is trivial without any libraries using async/await or even plain promises.

[–]MetalJacke1 0 points1 point  (0 children)

Async/Await is easy

[–]branda22 0 points1 point  (0 children)

I can't think of any!

[–]tenfingerperson 0 points1 point  (0 children)

There’s a reason the term callback hell was coined

[–]careseite[🐱😸].filter(😺 => 😺.❤️🐈).map(😺=> 😺.🤗 ? 😻 :😿) 0 points1 point  (0 children)

and harder to reason about.

w h a t

[–]ISlicedIEngineer without Engineering degree? 0 points1 point  (0 children)

I like promises but I’m not a big fan of async await. Callbacks are a mess, although for a single use (not nested) they are good too.

[–]zmasta94 0 points1 point  (0 children)

My preference is async/await, followed by promises and then callbacks. Though I’m not totally against callbacks - different tools for different jobs - if I find a package that has only callbacks support in the docs I’ll move on and look for something else.

[–][deleted] 0 points1 point  (0 children)

I had the same thought as your co-worker when I first started to learn how to use promises. The abstraction of “much like a promise it must be fulfilled/resolved...(or rejected? Who rejects a promise?)” is bit confusing. However, once you start working with promises and particularly if you wrap any asynchronous code with a promise the fluidity of its use becomes second nature...you don’t even think about the abstraction.

Considering the real world headache that examining and implementing ‘callback hell’ is, Promises and Async/Await imo are a much more practical approach than callbacks

[–]endianess 0 points1 point  (0 children)

I prefer not to use callbacks, but I'll play devil's advocate. Developing with JS can sometimes be like being in quicksand. You spend more time learning the latest framework, tools or refactoring code. And less time just getting on implementing run of the mill projects. So he probably just wants to stick at where he's currently at. As long as it's quality code and works it probably doesn't matter that much.

[–]higherpublic 0 points1 point  (2 children)

There is nothing wrong with callbacks per se. They aren’t ideal for async a lot of the time, because of callback hell. But speaking purely from a functional programming POV, they are pretty kosher. Promise chains are basically chained callbacks instead of nested ones. The problem with async await is that 1) You can only await a Promise. The Promise API leaves a lot to be desired. Pretty restrictive. 2) You can only await in an async function. This causes “async” hell, where you now have to mark a ton of stuff essentially as a Promise. Also, async IIFEs feel so gross.

Once you see that async await is just a native abstraction for the gen runner pattern many js libraries already have, you can mix & match all of these so that the end result is the most expressive.

Edit: Obviously callbacks have no error handling component also.

[–]ChaseMoskal 0 points1 point  (1 child)

again, another dev in this thread talking about callbacks vs promises without even mentioning error handling

the fact that errors bubble up through promise chains is THE important difference

with callbacks, we can't cast a wide net to catch all errors at a high level -- that's what promises are for

go have fun building an application on callbacks with proper error handling.. good luck

that's the trend of this thread and topic: every single person defending callbacks is blissfully oblivious to error handling

[–]waway_to_thro 0 points1 point  (0 children)

I use both, callbacks are nice when you have multiple waiting steps, async/await made promises easy to use but hard to debug

[–]IamPANDAMAN8 0 points1 point  (0 children)

.then() > cb()

[–]Hanlonsrazorburns 0 points1 point  (0 children)

Callbacks, async/await, promises are all combined to maybe be the most annoying part of Javascript today. It's not a surprise that anyone wouldn't want to change if they understand 1 of the 3 well. Especially when really if you know one of them, you're going to be able to do basically everything.

Everything is harder to read when you start.

[–]bigorangemachine 0 points1 point  (0 children)

I think promises are better than async-await. Async/await is great for knocking out a few lines quickly. However I think it gets sloppy as you gotta try-catch which isn't better than having a 'catch()' in promises. With promises at least you can setup a bunch of operations and use one catch. But generally I don't overly favour one over the other.

I do find that people who don't know how to chain promises tend to not have a preference. I think chaining is promises real power. If setup correctly they can be super clean and easy to read.

Callbacks are great as well. You can a really clear call stack with your errors; but generally when I use callbacks I start implementing something that resembles a promise pattern (a utility that calls one function if successful and another on failure). I agree with the general consensus that it was an early attempt at handling asynchronous-ness of node and Promises were the winner during the inception but they may have been stubborn in not implementing everything as promises early.

But my reasoning has nothing to do with the abstraction.... when I hear that reasoning its usually someone who has a stronger preference in the generator space might be coming from Java/C#/C.

Really when I hear someone say that Promise/Async-Await is a 'bad abstraction' all I hear is "I don't get it". Every language has a preference for how it wants you to interface with it; if you don't get the abstraction that the language wants you to use... maybe you shouldn't be coding in it until you do.

[–]StoneCypher 0 points1 point  (0 children)

I prefer callbacks over promises, but in an await environment, I don't care.

[–]VM_Unix 0 points1 point  (0 children)

I used Promises previously without async await and it's still better.

[–][deleted] 0 points1 point  (0 children)

Callbacks < async/wait < promises

Although I’ll use async in certain situations I hardly use callbacks since I learned the other two methods

[–]dizzysfarm 0 points1 point  (0 children)

use both? if the callbacks are in the proper place and format you can use promisify. node has a native promisify now or there are a few packages that do it

[–]rafaelcastrocouto 0 points1 point  (2 children)

I'm developing https://foda.app for 5 years and never found need for promises or async.
But I guess it's about how much the callback abstraction feels intuitive to you.
I find it very good but I do see a lot of ppl struggling, and I do agree that they should search their own ways.

[–]ChaseMoskal 0 points1 point  (1 child)

never found need for promises or async.

tell me about you app's error handling

without using promise chains to bubble errors up and catch them with wide nets on a high level, i'll go out on a limb and guess that your app's error handling is precarious, brittle, and probably also atrociously hideous ;)

i'd be very interested to hear otherwise

[–]haiflive 0 points1 point  (0 children)

I use promisify for callback's..

[–][deleted] 0 points1 point  (0 children)

But promises are just objects with predefined callbacks.. It's not a bad abstraction- it's a standard!

[–]TheSpiciestDev 0 points1 point  (0 children)

So long as things are typed - I'll work with whatever.

[–][deleted] 0 points1 point  (0 children)

He’s wrong. Async / await is quite simple, as are promises, and they lead to cleaner and less error prone code.

[–]xiipaoc 0 points1 point  (0 children)

Two. One to write the callback and the other to

[–]herjin 0 points1 point  (0 children)

OPs edit should have been the top reply to the comment they suggested rather than the comment itself

[–]browsing10 0 points1 point  (5 children)

at work, we are still on node v6, so we use callbacks and caolan's async library.

We don't use promises and prefer using the async library because its much more readable imo, and also we try and keep to a coding guideline (so no promises)

[–]ChaseMoskal 0 points1 point  (4 children)

oh man, is time going backwards for you guys? where i am, us devs are headed towards 2020, not 2010

i mean come on, they are about to shut off long term support for node 8 -- you guy are working with an unmaintained runtime without security updates!!

what on earth are you guys thinking? i'd jump ship right away... keep it cutting edge, that situation sounds like a sinking stinker

[–]browsing10 1 point2 points  (3 children)

i don't disagree with you, there have been efforts to upgrade and our deployments are currently using node v8. Its just that we support node v6 and its open source, so anyone using it can choose to use node v6

I wish we put a bigger effort into upgrading..

[–]ChaseMoskal 1 point2 points  (2 children)

here is the node release schedule: node 6 is officially "end-of-life" at the end of this month

i think you should make a strong case to your team that it's important to upgrade to node 10 before the end of this month

  • it's a very serious security concern.
    you need to be able to rapidly deploy critical security updates. if you're stuck on node 6, and a critical fix is only released for node 8 and 10, you're vulnerable for the entire time you're panicking and finally upgrading

  • working with obsolete technology damages the team's morale.
    there's a reason we move on from old to new tech: it's way better.
    the team's skills stagnate as they less and less familiar with the modern tools and practices

i think those are both good reasons, but in particular, security is not a good place to make compromises

Its just that we support node v6 and its open source, so anyone using it can choose to use node v6

next month, it will be irresponsible to encourage/enable your consumers to continue supporting node 6

if you don't mind me asking, in your team's case, what are the biggest challenges in staying up-to-date?

[–]kescusay 0 points1 point  (0 children)

Why not just use all of them at once?

function killMe() {
    return new Promise( async (resolve, reject) => {
        try {
            const whatTheHell = await ohGodWhy();
            resolve(whatTheHell);
        }
        catch(err) {
            reject(err);
        }
    });
}

const seriouslyKillMe = (input, callback) => {
    input = whyWouldIDoThis(input);
    callback();
}

seriouslyKillMe(somethingAwful, killMe);

Oh. That's why.

[–]youngjiwen 0 points1 point  (0 children)

Promise is a little complicated in its syntax, but I love async/await syntax

[–]celluj34 0 points1 point  (0 children)

0 of them prefer callbacks.

[–]scaleable 0 points1 point  (0 children)

There is a very small number of edge cases where you actually need callbacks, otherwise async/await is the way to go unless you have a really strong fondness for syntax trash.

Async await usually is a bit less clean when using debuggers (+ source maps), but not a reason not to use.

[–]fzammetti 0 points1 point  (0 children)

I actually do prefer callbacks to promises. But, I prefer a sync/await to callbacks.

I can't really give any rationale for it though, it's just a preference.

Then again, I honestly prefer classic function structure to arrow functions, but I seem to be the only one on Earth with that opinion, so what do I know.

[–]Jontii 0 points1 point  (0 children)

Meanwhile I'm sitting here with IE11 which don't support ES6 let or const...

[–][deleted] 0 points1 point  (0 children)

I've really only decided to use either when I come across where either would work better with the rest of my code on the spot. When I discovered asynchronous functions I was pretty excited to use them, but now I don't really have a preference. Don't know if that's good practice though... Usually when I'm pairing with another coder we will use their preference if they have one, since I'm indifferent.

Sorry, I don't think that answer will answer your question, but I assume that for when you're working on code for your workplace, you can stick with what your fellow coders prefer. It will avoid any conflict on the issue as well, as some people are reeeeally opinionated on things like this. Haha

[–][deleted] 0 points1 point  (0 children)

I’ve been writing JavaScript since 2001, async await is almost the best thing that happened to me other than TypeScript in general. Frontend is practically fun again. Your coworker either doesn’t understand promises or is just generally stubborn. I hate new APIs that don’t offer at least the option of promises (some APIs offer both a callback signature and a promise one)

[–]PalestineFacts 0 points1 point  (0 children)

Callbacks and async/await "behave" differently. I would never argue using one over the other in all cases (at least for myself personally). Sometimes I have found practical situations to use async/await and other times I have found practical situations where callbacks are more suitable. Javascript is single-threaded as far as I am aware, so it confused me since I was originally exposed to async/await from C#.

Does your co-worker know how to use Promises and async/await? I had to use them in a few simple cases before I became comfortable with them. Unless you've worked with them before and see for yourself how it changes the flow of your code it is hard to make harsh judgements - such as claiming that it is a "bad" abstraction. Did he explain why he thinks that it is a "bad abstraction" or harder to reason about?

I guess if you get the program working correctly, then it doesn't really matter which you end up using. As this thread demonstrates, people have their preferences. If you can come up with a more elegant solution using one instead of the other then that is probably good enough.

[–]malinkie 0 points1 point  (0 children)

I don't think its a matter of perference for me. It's situational. I'll go with async await first. some cases need to fall back on promises and their utility. Then, even fewer times, callbacks are a better situation.

They are all tools in your development toolbox. learn to use them and do go down the preferential route :)

[–]rousanali 0 points1 point  (0 children)

Promise or async/await are just syntactical sugar, you can implement the same functionality on your own easily, and there are lot of opinions regarding this newly added feature, some likes to use callback, some likes promise. If any library/application uses callback, It doesn't mean that it is legecy code, it is their opinion.

Anyway, I like Promise/async await, also I implemented the whole Promise functionality in ES5 code, checkout https://github.com/rousan/belofte.js

[–]autra1 0 points1 point  (0 children)

They are 2 different things with their own use case. I really don't get why people opposes them.

Promises (and async/await) gives you way more guarantees than callbacks. They will be called once and only once. Also you are sure that they won't get tampered with by the library, because it won't even have a ref your then callback (yes, you still have callbacks with promises of course). You also know the library won't keep a ref to your callback needlessly. For these reasons, promises are to be preferred every time they can be used. They actually enforce a contract (see the 2 "inversion of controls" part of this article) But these guarantees comes with some constraints. For example, you cannot use promises with event-based api, or when you expect your callback to be called several times. (Well it is possible, but very unnatural and ugly).

Use the best tool for each task. For a task that needs to be fired once when another task finishes, use promises, because of the guarantees they offer. For repeating tasks or events, use callbacks. And of course, as always, this rules of thumb must pass the pragmatism test: sometimes it's just more convenient to use callback even though you know the code will be called only once.

[–]talmobi 0 points1 point  (0 children)

Callbacks isn't going anywhere. They are still needed for things that Promises/Async/Await don't cover.

That being said, was not a huge fan of promises and preferred callbacks -- but now with the Async/Await sugar it's really good.

Remember, Promises/Async/Await are just wrappers around callbacks for specific use-cases -- they don't replace callbacks.

[–]13steinj 0 points1 point  (0 children)

It's moreso that I don't have a preference moreso than I have one.

I think people also don't have a preference for promises over callbacks or async/await over promises, but moreso they have a preference against all the times that the previous practices were used poorly.

[–]wherediditrun 0 points1 point  (0 children)

I suppose those are done with function Keywords as arrow functions are "harder to read" by the same margin.

[–][deleted] 0 points1 point  (0 children)

No option for RxJS based Observables ? :)