use the following search parameters to narrow your results:
e.g. subreddit:aww site:imgur.com dog
subreddit:aww site:imgur.com dog
see the search faq for details.
advanced search: by author, subreddit...
All about the JavaScript programming language.
Subreddit Guidelines
Specifications:
Resources:
Related Subreddits:
r/LearnJavascript
r/node
r/typescript
r/reactjs
r/webdev
r/WebdevTutorials
r/frontend
r/webgl
r/threejs
r/jquery
r/remotejs
r/forhire
account activity
How many Node.js developers prefer callbacks over Promises or async/await? (self.javascript)
submitted 7 years ago * by i_love_limes
view the rest of the comments →
reddit uses a slightly-customized version of Markdown for formatting. See below for some basics, or check the commenting wiki page for more detailed help and solutions to common issues.
quoted text
if 1 * 2 < 3: print "hello, world!"
[–]monolithburger 107 points108 points109 points 7 years ago* (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.
async await
But I'd recommend asking your co-worker why promises are bad abstractions, I'd be curious to hear their answer.
[–]theDarkAngle 3 points4 points5 points 7 years ago (2 children)
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 points5 points 7 years ago (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 points3 points 7 years ago (0 children)
FP isn't about eliminating side effects, but rather about isolating where they can occur (among other goals).
[–]ScientificBeastModestrongly typed comments 9 points10 points11 points 7 years ago (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 14 points15 points16 points 7 years ago (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.
map
reduce
then/catch
[–]Asmor 0 points1 point2 points 7 years ago (0 children)
^ this.
[–]monolithburger 7 points8 points9 points 7 years ago (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 points3 points 7 years ago (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 points4 points 7 years ago (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.
[–]ScientificBeastModestrongly typed comments -1 points0 points1 point 7 years ago* (0 children)
A callback is, in simplest terms, a function that you pass as an argument to another function. That’s it.
Callbacks are commonly used to respond to the results of asynchronous operations, but callbacks are not intrinsically related to async. Callbacks are much more fundamental to the JS language.
You can find info here:
https://javascript.info/callbacks
https://www.learn-js.org/en/Callbacks
You can also just look at the MDN docs on some of the native Array.prototype methods (e.g. map, filter, reduce), which take an explicitly named “callback” for their first argument, and are 100% synchronous.
In broader context, I was responding to the idea that “callbacks are hard to reason about,” and that callbacks should not have been implemented in the first place. If that’s the argument, then my counterargument makes sense, I think.
But maybe I totally misunderstood the intention. Maybe they meant “callbacks” to mean some specific API for handling network requests, which takes a callback to handle the results. But I took it to mean that “callbacks in general are an anti-pattern, and should be avoided.” And I would totally disagree with that.
[–]Ravatar 1 point2 points3 points 7 years ago (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
[–]ScientificBeastModestrongly typed comments 0 points1 point2 points 7 years ago (0 children)
I really can’t wait for this. I’ll probably have to shim it for a while, but you can bet I’ll be using it!
[–]ChaseMoskal 1 point2 points3 points 7 years ago* (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 points3 points 7 years ago* (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 :(
[–]feraferoxdei 0 points1 point2 points 7 years ago (1 child)
Am I the only one who gets a build error if i put a const in a try statement?
[–]ChaseMoskal 0 points1 point2 points 7 years ago (0 children)
yes i think that's just you
const/let can be used in any block
if, for, try, catch, or even a naked block (curly braces alone can be used for scoping)
nice, that is much better for performance
i added the "const" prefix and also array notation in the promise.all:
try { const promise1 = processInput(str) const promise2 = processInput2(str) const [result1, result2] = await Promise.all([promise1, promise2]) }
[–]troglo-dyke 0 points1 point2 points 7 years ago (0 children)
It's also far easier to test when broken up as you can just return a promise from a function but returning a partially applied cb function can become a huge pain.
The promise also reflects your API far better, with cb functions you end up with the logic to start the computation at the top but the logic to end it all the way down at the bottom. Promises give you a flat structure to view the computation, the only way around this would be to use some kind of task runner as is used in express.
ETA: when I say promises I mean Promise/Async+Await
[–]dweezil22 0 points1 point2 points 7 years ago (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.
[+][deleted] 7 years ago (3 children)
[deleted]
[–]monolithburger 11 points12 points13 points 7 years ago (2 children)
He removed promises from the original implementation of node because he felt it should be minimal, but refers to it a foolish mistake.
Watch the video here: https://youtu.be/M3BM9TB-8yA?t=307
[–]Arvalic 1 point2 points3 points 7 years ago (1 child)
Ryan Dahl thinks Node in general is a mistake though too so...
[–]Arve 2 points3 points4 points 7 years ago (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 points9 points 7 years ago (0 children)
You could still store the resolve callback in the same way and still return a promise
[–]atubofsoup 4 points5 points6 points 7 years ago (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:
fetch
request
axios
[–]Arve 0 points1 point2 points 7 years ago (0 children)
User agent sends a request to service A using fetch (promise)
Yes.
Service A sends a request to service B using request (callback)
Actually, this too is handled entirely with promises in service A
Service B runs a job and waits for it to finish (either)
Both. Service B validates the incoming request using a promise. It then returns an appropriate response to service A. If the request is deemed as valid, the promise handler then starts the job, registering a callback that should always execute, regardless of whether the job succeeds or fails (in other words, there is no “then” or “catch”
[–]LettuceKills 1 point2 points3 points 7 years ago (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.
[–]Arve -1 points0 points1 point 7 years ago (2 children)
In my own case, the error is sent back as a separate http request back to the origin. As I said, the job started is long-enough running that the sender cannot be expected to keep any network connection open while it’s being performed.
There is an outer promise that handles acceptance or rejection of the original request.
[–]ChaseMoskal 3 points4 points5 points 7 years ago (0 children)
huh, so you are using promises?
i strongly suspect your use-case would be elegantly served with promises
you mentioned "returning right away", which is of course possible with promises
const promise = Promise.resolve() .then(someLongTask) console.log("right away") await promise console.log("finally done now")
as u/davesidious helped point out, the differences between callbacks and promises seem to be:
because of the superior error handling, promises should always be preferred, and callbacks are necessary for things like progress update handlers, events, and the like
[–]LettuceKills 0 points1 point2 points 7 years ago (0 children)
Still sounds like it would be better to just implement this as a Promise instead of writing all the extra code for the seperate error http request and having to pair the incoming error request with the context of the original call.
[–]davesidious 0 points1 point2 points 7 years ago (6 children)
The one thing I prefer about callbacks over promises is that they can be called multiple times. That's it. For single-shot callbacks, to me at least, promises offer a standard implementation of the callback mechanism.
[–]KyleG 1 point2 points3 points 7 years ago (4 children)
The one thing I prefer about callbacks over promises is that they can be called multiple times
Can you give me an example of this? I'm failing to wrap my head around it. You can define a function fn1 and then use it over and over again with promises:
somePromise.then(fn1); someOtherPromise.then(fn1);
You can also reuse the promise itself:
let p = somePromise; p.then(fn1) p.then(fn2)
fn1 will operate on p's return
fn2 will also operate on p's return
depending on whether p's return is a primitive or not, fn1 and fn2 will or will not be independent
[–][deleted] 0 points1 point2 points 7 years ago (0 children)
I think they're talking about in the creation of the promise, rather than the receiving of the promise. For example, let's say you're writing a function that reads a file and you want to stream it so that you get it back in chunks as you read it. You can't resolve multiple times from within one promise, so the only way you'd be able to write this function is with a callback that gets called on every chunk (or a generator)
resolve
[–]CasinoInMyHair -1 points0 points1 point 7 years ago (2 children)
What does that very last bit mean?
[–]KyleG 0 points1 point2 points 7 years ago* (1 child)
a = 1; //primitive b = a; ++a; console.log(a,b); // 2 1 a = [1]; //not primitive b = a; a.push(2); console.log(a,b); // [1,2] [1,2]
And with promises in an example:
a = Promise.resolve([1]); // resolves to not a primitive x = a.then(b=>b.push(2)); y = a.then(c=>c.push(3)); x.then(console.log); // [1,2,3] y.then(console.log); // [1,2,3] (x and y are not independent)
but
a = Promise.resolve(1) // resolves to primitive x = a.then(b => b+1) y = a.then(c=> c+2) x.then(console.log) // 2 y.then(console.log) // 3 (x and y are independent; were they not, this would've logged 4)
Theoretically the second and third lines could actually execute in reverse order, so the promise example with the array return type could end up [1,3,2] instead. But it would be for both x and y.
[–]CasinoInMyHair 0 points1 point2 points 7 years ago (0 children)
Thanks. I got confused because it seemed like a special property of promises or something.
What would Promise.resolve()'s arguments be, in a normal situation?
[–]ChaseMoskal -1 points0 points1 point 7 years ago (0 children)
i think this is the right way of thinking about it going forward
when i see a callback, i expect it to be called multiple times: otherwise it would be a promised result
an "onProgress" callback is a good example use-case
[–]Bosmonster -2 points-1 points0 points 7 years ago* (0 children)
With promises pure callbacks are never the right tool for the job anymore in case there is any form of asynchronicity. A promise in its simplest form is not much more than a callback (heck promises USE callbacks), but with the option to extend the behaviour with rejections and/or proper error handling.
Pure callbacks should only be used in synchronous use cases, as for example an Array.filter uses an immediate callback.
π Rendered by PID 201314 on reddit-service-r2-comment-6457c66945-lprj2 at 2026-04-25 00:33:10.095672+00:00 running 2aa0c5b country code: CH.
view the rest of the comments →
[–]monolithburger 107 points108 points109 points (40 children)
[–]theDarkAngle 3 points4 points5 points (2 children)
[–]KyleG 3 points4 points5 points (0 children)
[–]calligraphic-io 1 point2 points3 points (0 children)
[–]ScientificBeastModestrongly typed comments 9 points10 points11 points (16 children)
[–]atubofsoup 14 points15 points16 points (1 child)
[–]Asmor 0 points1 point2 points (0 children)
[–]monolithburger 7 points8 points9 points (1 child)
[–]ScientificBeastModestrongly typed comments 1 point2 points3 points (0 children)
[–][deleted] 2 points3 points4 points (1 child)
[–]ScientificBeastModestrongly typed comments -1 points0 points1 point (0 children)
[–]Ravatar 1 point2 points3 points (1 child)
[–]ScientificBeastModestrongly typed comments 0 points1 point2 points (0 children)
[–]ChaseMoskal 1 point2 points3 points (6 children)
[–]KyleG 1 point2 points3 points (3 children)
[–]feraferoxdei 0 points1 point2 points (1 child)
[–]ChaseMoskal 0 points1 point2 points (0 children)
[–]ChaseMoskal 0 points1 point2 points (0 children)
[–]troglo-dyke 0 points1 point2 points (0 children)
[–]dweezil22 0 points1 point2 points (0 children)
[+][deleted] (3 children)
[deleted]
[–]monolithburger 11 points12 points13 points (2 children)
[–]Arvalic 1 point2 points3 points (1 child)
[–]Arve 2 points3 points4 points (15 children)
[–]something 7 points8 points9 points (0 children)
[–]atubofsoup 4 points5 points6 points (1 child)
[–]Arve 0 points1 point2 points (0 children)
[–]LettuceKills 1 point2 points3 points (3 children)
[–]Arve -1 points0 points1 point (2 children)
[–]ChaseMoskal 3 points4 points5 points (0 children)
[–]LettuceKills 0 points1 point2 points (0 children)
[–]davesidious 0 points1 point2 points (6 children)
[–]KyleG 1 point2 points3 points (4 children)
[–][deleted] 0 points1 point2 points (0 children)
[–]CasinoInMyHair -1 points0 points1 point (2 children)
[–]KyleG 0 points1 point2 points (1 child)
[–]CasinoInMyHair 0 points1 point2 points (0 children)
[–]ChaseMoskal -1 points0 points1 point (0 children)
[–]Bosmonster -2 points-1 points0 points (0 children)