Async includes a module called Monitor that lets you correctly handle exceptions in async code. The signature of one of the most commonly used methods is this:
It takes two arguments. Name is used to tell you what monitor the error was caught by. The second is a function that returns some kind of Deferred.t. The whole thing returns a (deferred) Result.t (: [`Ok of 'a | `Error of exn]), letting you either do something with the results of the async call or handle the error. Usage might be something like this:
(* query_or_print : Db.t -> Db.Query.t -> Db.Result.t option Deferred.t *)
let query_or_print db query =
Monitor.try_with (fun () -> Db.query db query)
>>| function
| `Ok result -> Some result
| `Error e -> print_endline (Exn.to_string e); None