Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Handle deferred processing when document is hidden #402

Merged
merged 26 commits into from
Oct 13, 2022

Conversation

afshin
Copy link
Member

@afshin afshin commented Sep 11, 2022

This PR does the following.

  • Modifies the behavior of the polls' when-hidden standby to tick once after being hidden (the default options.linger value of 1 time can be overridden at instantiation time)
  • Modifies the MessageLoop to use a new promise-based implementation of schedule for immediate deferral to support always running in background tabs
  • Modifies Poll to export a protected hidden attribute to indicate to sub-classes when the document is hidden.

We can backport this to 1.x as well.

CC: @thetorpedodog – I kept your suggested setTimeout for Poll instances because almost all polls have a nonzero interval anyway and it simplifies that code. I updated MessageLoop to work in the background without having to resort to setTimeout.


Fixes #400

@afshin afshin added bug Something isn't working enhancement New feature or request labels Sep 11, 2022
@afshin afshin added this to the Lumino 2 milestone Sep 11, 2022
@afshin afshin self-assigned this Sep 11, 2022
@afshin afshin changed the title Defer immediate Handle deferred processing when window is hidden Sep 11, 2022
@afshin afshin added the performance Addresses performance label Sep 11, 2022
@afshin afshin changed the title Handle deferred processing when window is hidden Handle deferred processing when document is hidden Sep 11, 2022
@afshin afshin force-pushed the defer-immediate branch 2 times, most recently from 16c2f2f to a02d471 Compare September 11, 2022 18:07
Copy link
Contributor

@blink1073 blink1073 left a comment

Choose a reason for hiding this comment

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

Thanks!

@afshin afshin marked this pull request as draft September 12, 2022 13:22
@afshin afshin force-pushed the defer-immediate branch 2 times, most recently from d226865 to e764dfa Compare September 12, 2022 15:32
@afshin afshin marked this pull request as ready for review September 12, 2022 16:00
@vidartf
Copy link
Member

vidartf commented Sep 14, 2022

[...] schedule uses setTimeout when the window is hidden and uses requestAnimationFrame/setImmediate otherwise

What are the nuances of this? If a window is visible, and I requestAnimationFrame and then hide the window, will the trigger fire the next time I show the window? If that is the case, it seems like this will cause some inconsistent behavior?

Also: Do we know what was the motivation for choosing requestAnimationFrame rather than setTimeout for the message loop originally? It seems like not driving the page when it is hidden is considerate of system resources, and this will change that.

@afshin
Copy link
Member Author

afshin commented Sep 15, 2022

I think the primary original motivation wasn't about consideration of system resources so much as it was about responsiveness. Consider this example somebody created that is very illustrative: https://codepen.io/YOONBYEONGIN/pen/RwxrEPN

It seems pretty unlikely that you could hide the window in the time between when you invoke requestAnimationFrame and the time the function actually fires.

@vidartf
Copy link
Member

vidartf commented Sep 16, 2022

It seems pretty unlikely that you could hide the window in the time between when you invoke requestAnimationFrame and the time the function actually fires.

@afshin It might be unlikely, but if it happens once a month for a heavy user, and the consequence is catastrophic, then it still matters. So the question the becomes, what will be the failure mode if one callback gets stuck waiting on an animation frame while other callbacks keep firing with timeouts? Do we end up processing messages out of order? I don't think "probably nothing bad will happen" is an ok answer in this case. Waiting a bit for something after resuming a browser tab is in general a more ok user experience than undefined behavior (you can fit a lot into "undefined behavior").

@afshin
Copy link
Member Author

afshin commented Sep 17, 2022

I should have stated that more strongly: you have <1ms to switch tabs from the time you have invoked an animation frame to the time it fires. The only time unschedule() is invoked by the message loop logic, it is in the flush() function, which is invoked by Lumino code only to support older IE and Edge. It's not actually invoked otherwise, so I am not coming up with a scenario I can imagine where it causes loop events ever firing in the incorrect order or leading to a catastrophic outcome.

I'll give it some more thought.

Does anyone else who has considered this part of the code base have any thoughts?

@afshin
Copy link
Member Author

afshin commented Sep 17, 2022

if one callback gets stuck waiting on an animation frame while other callbacks keep firing with timeouts

Hm. You're right, @vidartf. This is possible. I put the PR back in draft mode.

Perhaps client code needs to specify whether it wants to run when the document is hidden (slower because it is setTimeout but runs in background tabs as well), or be strictly tied to animation frames and only run when the document is visible.

@afshin afshin marked this pull request as ready for review September 22, 2022 13:49
@afshin afshin requested a review from vidartf September 22, 2022 23:15
@afshin afshin force-pushed the defer-immediate branch 4 times, most recently from 4ed8402 to b096c8e Compare September 27, 2022 13:27
@afshin
Copy link
Member Author

afshin commented Sep 27, 2022

After a lot of back and forth, this is ready for review.

Copy link
Contributor

@blink1073 blink1073 left a comment

Choose a reason for hiding this comment

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

LGTM!

Copy link
Member

@fcollonval fcollonval left a comment

Choose a reason for hiding this comment

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

Thanks @afshin

I have two comments.

packages/polling/src/poll.ts Outdated Show resolved Hide resolved
packages/polling/src/poll.ts Show resolved Hide resolved
Co-authored-by: Frédéric Collonval <fcollonval@users.noreply.github.com>
@fcollonval
Copy link
Member

@afshin should we move forward with this one?

rejected = true;
};
}
)(Promise.resolve());
Copy link
Member

Choose a reason for hiding this comment

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

As mentioned in last weeks lab dev meeting, using a Promise or async will schedule the task as a microtask, compared to requestAnimationFrame / setTimeout etc that will schedule it as a macro task. This difference is likely to be significant (@afshin has the details).

Copy link
Member Author

Choose a reason for hiding this comment

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

It is safe in this circumstance because message propagation is both lightweight and synchronous. This is the reason why the same technique is unavailable to us in Poll.

Copy link
Member Author

Choose a reason for hiding this comment

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

@afshin should we move forward with this one?

Yes, I wanted to give @vidartf a chance to look, but I'm happy to move ahead.

@afshin afshin merged commit 0614f7f into jupyterlab:main Oct 13, 2022
@github-actions github-actions bot locked as resolved and limited conversation to collaborators Oct 14, 2023
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
bug Something isn't working enhancement New feature or request performance Addresses performance
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Handle deferred processing when document is hidden
4 participants