1. You can `throw` in async functions (when things are promisified) and `.catch` async errors.
2. You can't forget the error handling or ignore the `err` parameter, it will propagate until you handle it.
3. Did I mention propagation like with sync errors yet?
4. It's easy to build exception hierarchies. Libraries like bluebird provide typed and predicate catch blocks that let you do exception handling the way you want like in other languages.
5. Did I mention async/await or coroutines yet? You can use normal try/catch with promises as a language feature.
6. No Zalgo, if something might be async it should always be async to preserve execution order. `then` always runs asynchronously which means no timing issues and race conditions in errors and success values.