What are "Callbacks"?
A callback function is usually used as a parameter to another function.
The function that receives callback funct...
For further actions, you may consider blocking this person and/or reporting abuse
I agree. Also, if your next function happens to depend on results from the previous function, you can chain them without nesting:
Nice article overall though, keep it up :).
Yes, this would be a good approach too :)
You can have one
catch
clause below allawait
sBut what if we have all the promise dependend on promise above them, and we want to handle error for each of them?
Perhaps you meant something else - in which case chaining the error handlers may be worth considering:
The thing is if you present code written in the manner described you have to expect to raise some eyebrows because you are ignoring error codes. Obviously you felt the need to do this - however work arounds like this are symptomatic of poor error design.
Defensive programming is messy
So messy in fact that the designers of Erlang invented "Let it Crash" (Programming Erlang 2e, p.88):
However most runtimes don't have the luxury of letting the process die and having the supervisor deal with the crash (p.199):
However there is another point to be made - not all errors are equal. Roughly speaking:
Not all languages have exceptions but they have idioms for exceptional errors. Golang:
Rust has the error propagation
?
operator.i.e. for
Ok(value)
thevalue
is is bound to the variable while anError(error)
is returned right then and there.When a language like JavaScript supports exceptions the rule of thumb tends to be:
So when we see
areasErr
is an expected error and should be handled, not ignored. And just because an error code is returned doesn't necessarily imply thatgetAreas()
can't be a source of unexpected errors. When we seethe code is implying that there aren't any expected errors to be handled locally but
getAreas()
can still be a source of unexpected errors.With this in mind - 4.1.3. Rejections must be used for exceptional situations:
i.e. a promise should resolve to an error value for expected errors rather than rejecting with the expected error. So when we see
there is a bit of a code smell because all errors deemed by
getAreas()
as exceptional are converted to expected errors at the call site and then are promptly ignored. There is an impedance mismatch between howgetAreas()
categorizes certain errors and how the code using it treats them. If you have no control overgetAreas()
then an explicit anti-corruption function (or an entire module for a "layer") may be called for to reorganize the error categorization (and the associated semantics), e.g. :so that the consuming code can be plainly
Compared to the above
comes across as expedient (though noisy) and perhaps receiving less thought than it deserves.
There's an npm library that does exactly this. (Full disclosure: I wrote it and have been using it for a couple of years)
npmjs.com/package/@jfdi/attempt
All approaches have their trade-offs.
The Zen of Go:
Go's errors are values philosophy is a recognized pain point.
Handling errors where they occur - midstream - obscures, in use case terminology, the happy path/basic flow/main flow/main success scenario. That doesn't mean that other flows/scenarios aren't important - on the contrary. That's why there are extensions/alternate flows/recovery flows/exception flows/option flows.
Clearly identifying the main flow in code is valuable.
Whether or not an IDE can collapse all the conditional error handling is beside the point - especially given that traditionally Java has been chastised for needing an IDE to compensate for all its warts.
Granted Go doesn't use a containing tuple like
[error, result]
but destructuring still directly exposesnull
orundefined
values which is rather "un-functional" - typically the container (the tuple) is left as an opaque type while helper functions are used to work with the contained type indirectly (e.g. Rust's Result).Now your criticism regarding
try … catch
is fair …… but JavaScript isn't a functional language (which typically is immutable by default and supports the persistent data structures to make that sustainable). However it is possible to take inspiration from Rust:
Rust: A unique perspective
e.g. for local variables with exclusive/unique access (i.e. aren't somehow shared via a closure) mutation can be acceptable.
So maintaining a
context
object to "namespace" all the values that need to stretch across the varioustry … catch
scopes isn't too unreasonable:Thank you for sharing <3
But what if we have all the promise dependend on promise above them, and we want to handle error for each of them?
Simple and powerful solution 👌
the "standardized error handling function" may seem nice, but it forces you to handle each error individually, you need to check the error for every statement with if else, resulting if else tower of terror and we are back to square one
meanwhile promise.all did a better job in handling all error with one catch
Yes, but they are promises not functions returning promises.
You just need to pass the function(Promise) references in Promise.all