Given our recent performance push, I thought I'd bring this topic up again.

When writing loops using async functions in browser code, it's still very easy to cause jank. For example, a trivial function:

> async function janky() {
>   for (let i = 0; i < 100000; i++) {
>     await Promise.resolve();
>   }
> }
>
> janky().then(() => console.log("done"));

will cause the browser to hang. While that's not considered a bug in the implementation of the promise scheduler, and might not be particularly surprising in that trivial example, lots of real-world code can still hit this case - which both isn't obvious from casual inspection, and even if it was, doesn't have an obvious solution.

Concretely, we struck this a couple of years ago in bug 1186714 - creating a backup of all bookmarks ends up looking alot like the loop above. In addition, the Sync team is moving away from nested event loops towards promises, and our work to date is hitting this problem.

In bug 1186714, we solved the problem by inserting code into the loop that looks like:

>    if (i % 50 == 0) {
>      await new Promise(resolve => {
>        Services.tm.dispatchToMainThread(resolve);
>      });
>    }

http://searchfox.org/mozilla-central/rev/f55349994fdac101d121b11dac769f3f17fbec4b/toolkit/components/places/PlacesUtils.jsm#2022

so we explicitly yield to the event loop every 50 iterations. However, this isn't optimal as the 50 is obviously a magic number, determined by experimentation on a couple of machines - when running on low spec hardware, this loop is almost certainly still janky. If we try and err on the side of caution (eg, yielding every iteration) we see the wall time of the loop take a massive hit (around twice as long in that bug).

I'm wondering if there are any ideas about how to solve this optimally? Naively, it seems that the (broadest sense of the term) "platform" might be able to help here using it's knowledge of the event-loop state - eg, a function that indicates "are we about to starve the event loop and become janky?", or possibly even the whole hog (ie, "please yield to the event loop if you think now is a good time, otherwise just hand back a pre-resolved promise").

Or maybe there are simpler options I've overlooked?

Thanks,

Mark
_______________________________________________
dev-platform mailing list
[email protected]
https://lists.mozilla.org/listinfo/dev-platform

Reply via email to