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.
> 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.
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.