I’m not all caught up on the Promise plans, but for Unhandled Promise Exceptions, I think it depends on if using type Promise.t('a, 'e)
, we still have Promise.error
… or if, as discussed, somehow the exception goes up into the 'e
. It’s difficult because you can reject
with a useful error value but anything below that promise can just throw at any point… so that type of 'e
really needs to be something like type t('e) = | Cause('e) | Exception(Js.Exn.t)
.
We also now just avoid promises entirely because Promise.error
is not very useful. We use callbacks for exceptions we actually want to track.
I know Relude and ReScript don’t have the same goals, but this is exactly what we used Relude.Void.t
for. Say you have Promise.t('a, 'e)
, you handle the error with a Promise.catch
… you end up with Promise.t('a, Void.t)
… meaning the error is handled. At the “end” of your app logic when you need to go back into ffi, type it to only accept Promise.t('a, Void.t)
and you know you have no unhandled errors. Could even call it Promise.handled
if Void.t
is too scary.
Result.t<Promise.t<Result.t<'a, 'e>>, 'e>
We had this problem a few times when we first started down this path. Generally the types will just stop you, but I think it mostly has to do with how confusing it is to have to do so much manual promise wrapping and unwrapping all of the time without any real help. Working directly with Promises of Results, we ended up with like 3x more code noise… completely unhelpful, masks logic, and invites errors.
We solved this, like I mentioned above, by creating a Zio.t
type to represent it. Once we wrote helper functions specific to this type, like Zio.try
and Zio.flatMap
(I know, I know…) this issue completely disappeared. Outside of onboarding someone new to this pattern, it never comes up. (But, also, as I wrote above, we did stop using promises entirely.)
Usually our code is something like:
10 lines of ffi (input)
1000 lines of application logic
10 lines of ffi (output)
This isn’t so much a “ReScript/JavaScript” thing as much as it is a “App logic/Implementation details” thing. For us, promises only matter for ffi and are an annoying implementation detail forced on everywhere else. We optimize for the 1000 lines of constantly changing, and important to our business, app logic instead of the 10 lines of ffi we write once. Promise is not a very useful type for App logic. So turn it into a helpful type at input FFI and never thinking about the structure of it again until we need to undo that the output ffi.