Skip to content

Conversation

@marcaaron
Copy link
Contributor

@marcaaron marcaaron commented Oct 5, 2020

This PR should help organize the reconnection logic and event listeners that were previously hanging out in API. I also removed the logic that would turn off the AppState listener on web and desktop since I initially figured it would be too aggressive. But with reports that things are not showing in the LHN, maybe it's not a bad idea to be aggressive about reconnect callbacks firing.

Update:

Also, added a mechanism to help detect when a laptop has been closed. This is not something that we get with NetInfo or the Network Information API it depends on.

Curious to get some thoughts @cead22 @AndrewGable @tgolen

Fixed Issues

Fixes https://github.com/Expensify/Expensify/issues/142726
Fixes https://github.com/Expensify/Expensify/issues/141817

Tests

Screenshots

@marcaaron marcaaron self-assigned this Oct 5, 2020
cead22
cead22 previously approved these changes Oct 5, 2020
triggerReconnectionCallbacks();
}
lastTime = currentTime;
}, 2000);
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sounds like a good thing to try!

@marcaaron marcaaron marked this pull request as ready for review October 5, 2020 22:34
@marcaaron marcaaron requested a review from AndrewGable as a code owner October 5, 2020 22:34
@marcaaron marcaaron changed the title [WIP] Follow up to reconnection callback fix Follow up to reconnection callback fix Oct 5, 2020
@marcaaron
Copy link
Contributor Author

Heads up, I earmarked a fix for https://github.com/Expensify/Expensify/issues/141817 into this PR

If anyone objects to that I'm happy to separate it into a new PR.

@marcaaron marcaaron changed the title Follow up to reconnection callback fix Follow up to reconnection callback fix / Fix reconnect methods not firing on laptop awakening Oct 5, 2020
triggerReconnectionCallbacks();
}
lastTime = currentTime;
}, 2000);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sounds like a good thing to try!

Copy link
Contributor

@cead22 cead22 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Curious for everyone's thoughts on my comment

// every two seconds and if the last time recorded is greater than 5 seconds
// we know that the computer has been asleep.
lastTime = (new Date()).getTime();
sleepTimer = setInterval(() => {
Copy link
Contributor

@cead22 cead22 Oct 6, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it worth using setTimeout instead of setInterval (and maybe establishing this as a best practice for JS)?

This is an old "best practice" but I think it's still valid, and the idea behind it is that if there's an uncaught error that prevents clearInterval from running, this function will be running in a "loop" forever.

To avoid that, I think we can use setTimeout instead like so:

We define it as a recursive function that calls setTimeout

function listenForReconnect() {
...

    // define it
    var checkConnectivity = () => {
        setTimeout(() => {
            const currentTime = (new Date()).getTime();
            if (currentTime > (lastTime + 5000)) {
                triggerReconnectionCallbacks();
            }
            lastTime = currentTime;
            checkConnectivity();
        }, 2000)
    };

    // call it once
    checkConnectivity()
    ...
}

And when we stop listening, we re-define it as a no-op

function stopListeningForReconnect() {
    // make it a no-op
    checkConnectivity = () => {};
    ...
}

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's a really interesting point. And I guess my only question would be what exactly would cause the clearInterval() to never run in this case? We are calling it via redirectToSignIn(), but I'm not seeing how (or which) exception would prevent it from running. And if there was such an exception then wouldn't that same exception prevent you from setting checkConnectivity as a no-op?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what exactly would cause the clearInterval() to never run in this case?

I don't know, it's mostly a defensive measure in case we ever write a bug that causes an error that isn't caught

And if there was such an exception then wouldn't that same exception prevent you from setting checkConnectivity as a no-op?

Duh, yeah 🤦. I was looking for the right way to do this since I haven't thought about it in a while, so clearly this isn't it, but I bet there is a way. I'll look into it, and will merge this now

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh actually, I think what happens is that if there's an uncaught exception and we don't make checkConnectivity a no-op, it doesn't really matter because we didn't call setInterval, and thus, the execution of checkConnectivity will be interrupted too. Does that make sense, and do you agree with that being how it'd work?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, I think I get it now! You are saying that if something inside the checkConnectivity method fails then we will stop looping. Therefore it's better to use recursion here since we don't want to keep calling a method that we already know is going to fail.

I think that makes sense, but another perspective might be that setInterval is only bad if we allow these unhandled errors to exist unhandled. Consider this case:

myInterval = setInterval(() => {
	try {
        breakStuff();
    } catch(err) {
        clearInterval(myInterval);
        throw err;
    }
}, 1000);

I like this better because we don't need to no-op anything or use recursion. If we encounter an error then we just decide to stop the loop and throw the error.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I like that idea of using a try/catch. I think that's cleaner than setTimeout. I'd also like to see that added as a utility like safeSetInterval or something (though not necessary for this).

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You are saying that if something inside the checkConnectivity method fails then we will stop looping.

No, not really. I think any uncaught error can stop the execution of JS code, is that not the case with React?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

React is JavaScript and follows the same rules. The suggestion that an uncaught error from anywhere could somehow stop a recursive setTimeout() I think is wrong? Timers have their own execution context and can only be "broken" if an error is thrown inside the timer.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yup, not from anywhere, but if triggerReconnectionCallbacks(); or anything that that function calls throws, then the interval won't be cleared. So the try/catch you propose should work well

@cead22 cead22 merged commit aa95bfc into master Oct 6, 2020
@cead22 cead22 deleted the marcaaron-followUpAuthToken branch October 6, 2020 20:26
@MelvinBot MelvinBot mentioned this pull request Jan 29, 2024
50 tasks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants