If you're catching exceptions all over the place, or worse using them for flow control as part of normal operations, you're doing it wrong. Exceptions should indicate a major error that you can't easily recover from and as such should be caught and logged at the top of the stack, i.e. the main thread run method or request handler.
When used that way, they give you very useful information as to what went wrong and where, while making your program more robust and resilient to errors. We've found this to be the case time after time at https://starthq.com, which runs on Node but uses fibers via https://github.com/olegp/common-node.
"Exceptions should indicate a major error that you can't easily recover from"
Maybe in Java and C++, but in Common Lisp we have restarts that allow you to recover from an exception. I like to use the example of attempting to write to a file when the disk is full, because:
1. It is possible to recover from the exception (e.g. ask the user to delete some files)
2. It makes no sense for the I/O library to do all the things needed to recover
3. It is a maintenance headache for client code to do all the things needed to recover
With restarts things would look like this: the I/O library would set up a restart for write that would retry the operation, the client code would catch the exception, prompt the user to free some space, and when the user indicates the space is free the restart is invoked. The I/O library knows the right way to restart the operation, and client code knows whether or not that should happen, and you get code that does not just quit over a disk being full.
I think most people have been brain damaged by checked exceptions in Java. It comes with this expectation that need to have to catch block in almost every single function. But if you do that, you've just recreated error codes and multiple returns!
Exceptions are meant be thrown frequently and caught very infrequently. Catch in the few places where recovery is possible and where you can log the error. That's it.
If you're catching exceptions all over the place, or worse using them for flow control as part of normal operations, you're doing it wrong.
That is a subjective view, and certainly not a universal one. In Python, for example, exceptions are routinely used for flow control purposes; see StopIteration.
When used that way, they give you very useful information as to what went wrong and where, while making your program more robust and resilient to errors. We've found this to be the case time after time at https://starthq.com, which runs on Node but uses fibers via https://github.com/olegp/common-node.