There's a lot of old cruft using async (my current workplace being one of them) - bluebird also has 4x the downloads of async.
async is pretty terrible though, you cannot pass the results from one execution to another, i.e. passing results from one function in async.series to another, which results in developers tending to pollute variable scope by defining above and filling it in inside each callback. This prevents the ability to write clean isolated testable functions.
You can return the first from a function and it will be a promise. Promises are reusable. Also they contain exceptions in their context. Those can be handled at any point of the promise-chain.
So promises are way more composable than callback based solutions.
Nobody pretended that async didn't exist, we just knew its the best "solution" that ignored the problem.
Which was: throwing away the entire language's compositional features, including the concept of input arguments and return values, results with... poor compositionality, of course.
Yes, uncompositionality of callbacks leads to callback hell. Or to reinventing every single thing but for callbacks. Like array.map (which works with promises) or array.forEach (also works with promises) or every single synchronous function (they all work when passed to promises).