DEV Community

Discussion on: How do you handle the disposable IF statements ?

Collapse
 
qm3ster profile image
Mihail Malo

If I understand the frustration here correctly, I would like to draw a parallel with repeatedly revalidating "known-good" data.
There, the solution is separating the "input" data from the "validated" data at the type level, so you work with the primitive types once per acquisition, and then your application logic can avoid checks completely. It kind of lives in an environment guarded of stupidity.

A similar pattern here is carrying out the checks only when the state could have changed. For example, it might be beneficial to check isAppProperlyInstalled in an init step, before starting the main loop of a program. Not only do you want to fail as quickly as possible, but it will make all other modules simpler, since they can assume that by the time they are loaded, all of the sanity checks and conditions are in order.

In other cases, you may have to have some functionality working before an asynchronous condition has been verified. For example, user.isLoggedIn, which might go to the network if a token is expired, etc. In this case, you can do a kind of RAII, and return a promise of the logged in user, instead of having a user whose state may change. This can be introduced in parallel with old checks:

const getUser = async () => {
  if (user) return user
  if (legacyUser.isLoggedIn) return user = wrap(legacyUser)
  return new Promise((resolve, reject) => {
    legacyUser.logIn((err, newUser) => {
      if (!err) resolve(user = wrap(legacyUser = newUser))
      else reject(err)
    }
  }
})
Collapse
 
mazentouati profile image
Mazen Touati

Sounds interesting. There's some great hints here, but a similar solution has to be found for each language as the asynchronous model not available for every one of them. Though, I found out that every disposable IF should be treated separately. As there's no general rule to handle them all in the same way. For example and per your suggestion, the app installation check IMO should happen before starting the server using hooks or something similar. Thanks Mihail !

Collapse
 
qm3ster profile image
Mihail Malo

You could still write this with callbacks, in fact, getUser could be a synchronous callback in some languages, blocking the (green)thread.

The general idea is that you are "scheduling" work for after the asynchronous condition is determined, and keeping a "handle" to the work to know if/when it succeeds. This could be a part of the language runtime, or directly in your application code, where there is more opportunity to retry/batch/time out your pending "tasks".

But yeah, the initialization phase, including critical assertions, is a totally separate story.

Thread Thread
 
mazentouati profile image
Mazen Touati • Edited

In the second paragraph, are you talking about " job queue " ?

Thread Thread
 
qm3ster profile image
Mihail Malo

Yeah.

For example the way you schedule network actions with an
offline-first/optimistic update system like Apollo, the Firebase client, etc.

Chaining on an actual promise of a network request or filling a stack of "tasks" which will begin sending to the network after a "cameOnline" poll or callback fundamentally represents the same idea: Preparing now actions that may run in the future if conditions are right.

Thread Thread
 
mazentouati profile image
Mazen Touati

I see, thanks for sharing your thoughts Mihail 😊