Why do we use callbacks in asynchronous functions (javascript)?


I am stuck with callbacks in Javascript and asynchronous programming.
So I get what callbacks are and how they are used, but I dont understand why we need and use them?
So Javascript is single-threaded, event-driven. It follows line by line, if you have code that is blocking,
than we have to wait to finish it, so asynchronous is a solution to it. Start now and finish later, while the rest of the code (e.g. functions) are executed, if it is synchronous. if the async operation is finished, then it will be executed. Inside we have the callback function and gets called back/executed.

Why I don’t take the return value of the async operation and return it or assign it to a variable and then reuse it? Is this not much better than the callbackhell? Why even we do something like this?

What I heard so far, but didnt understand:
-Hiding Implementation
-Flow control, execute code only if async operation is finished
-Callback needs data from the async operation e.g. http request to an api endpoint (but this could be done differently see above)

Maybe someone can help me out

Thanks and greetings

Strictly opinion to follow; keep some grains of salt handy.

Callbacks are a blessing in that they permit and encourage expressive programming which tends more toward declarative than imperative paradigms. Yes, they do hide some or much of the implementation, which is not such a bad thing when we consider the abstraction of the confusing bits so that our expression is more readily understood by the human reader.

JavaScript treats functions as first class objects which means they can be used anywhere an object can be used. Fair enough, we can pass a function to another function, either invoked, or by reference alone. With prototypal inheritance there is a lot we can do to extend our code base, including functions, though one need not get into that, just now. Over my head, largely.

Encapsulation is a really cool feature to have and it would not exist if we could not return a function from a function. It’s this principle that gives us closure. Track down some of the discussion on Higher Order Functions and you are bound to hit upon one or two that discuss closure.


const lines (m, b) {
    return x => m * x + b;

This function takes two parameters, m and b and returns a function of x with those values written in as constants. We can essentially define any line equation, and then reuse that equation as often as we need while only ever supplying x. Beautiful stuff, especially when implemented dynamically with practically no foot print.

DRY is something that expressive code adheres to readily as it limits the number of repeated code patterns. Some repetition is to be expected but the more we can abstract away, the less clutter we are left with in our main flow, and the more we limit repetition.

As for control flow, ASYNC works with method chaining, does it not? The only control flow in each phase will be the success/failure logic.

These are hardly serious points, just something to ponder while we wait for a more definitive answer to your post.