If you have broken your code up into small enough modules and functions, and you know which tasks depend on other tasks, you will not have callback hell.
If you are unclear on the logic of your code, you can often make it work in a messy way with the "laundry list approach". This involves formulating a sequence of tasks that happens to work, without needing to understand the actual dependencies. Many synchronous languages facilitate a laundry list approach, as their inefficient blocking paradigm removes the need to formulate the laundry list as a sequence of nested callbacks. It's still messy code.