On Fri, May 19, 2017 at 4:18 AM, Mark Hammond <mhamm...@skippinet.com.au> wrote:
> On 5/18/17 12:03 PM, Boris Zbarsky wrote:
>>
>> On 5/17/17 9:22 PM, Mark Hammond wrote:
>>>
>>> I'm wondering if there are any ideas about how to solve this optimally?
>>
>>
>> I assume
>> https://w3c.github.io/requestidlecallback/#the-requestidlecallback-method
>> doesn't have quite the right semantics here?  That would let you run when
>> the browser is idle, and give you some idea of how long you can run for
>> before you should yield.
>
>
> I didn't quite expect this to work, but by abusing rIC I can almost make
> something work - I just have the callback stash the IdleDeadline object and
> return immediately, but continue to refer to it anyway. eg:
>
> let idleChecker = {
>   resolved: Promise.resolve(),
>   deadline: null,
>   promiseIdle() {
>     if (this.deadline && this.deadline.timeRemaining() > 0) {
>       return this.resolved;
>     }
>     this.deadline = null
>     return new Promise(resolve => {
>       window.requestIdleCallback(deadline => {
>         this.deadline = deadline;
>         resolve();
>       });
>     })
>   }
> }
>
> async function janky() {
>   let start = Date.now();
>   for (let i = 0; i < 1000; i++) {
>     await Promise.resolve();
>     await idleChecker.promiseIdle();
>   }
>   console.log("took", Date.now() - start)
> }
> janky().then(() => console.log("done"));
>
> I 1/2 expect this to defeat the intent/implementation of rIC, but it does
> work, causing ~2x-5x slowdown of the loop. I wonder if this is worth
> experimenting with some more?
>
> Mark
>

So if you have a look at how the idle callback algorithm is defined[1]
and what timeRemaining is supposed to return[2] you see that
timeRemaining doesn't update its sense of idleness, it only concerns
itself with the deadline. So if you save the IdleDeadline object and
resolve early, then timeRemaining won't know that the idle period
entered upon calling the idle callback might have ended.

I do think that you need to invert this somehow, actually doing the
work inside a rIC callback. Something like[3]:

  let idleTask = {
    total: 100000,
    progress: 0,
    doWork: async function(deadline) {
      for (; this.progress < this.total && deadline.timeRemaining() >
0; ++this.progress) {
        await Promise.resolve();
      }
      console.log(this.total - this.progress)
    },
    schedule(deadline) {
      this.doWork(deadline).then(() => {
        if (this.progress < this.total) {
          requestIdleCallback(this.schedule.bind(this));
        } else {
          this.resolve();
        }
      })
    },
    run() {
      this.resolve = null;
      return new Promise(resolve => {
        this.resolve = resolve;
        requestIdleCallback(this.schedule.bind(this));
      })
    }
  }

  async function janky() {
    var start = Date.now();
    await idleTask.run();
    console.log("took", Date.now() - start)
  }

  var handle = setInterval(function (){ console.log("I shouldn't be
blocked!")}, 500)
  janky().then(() => console.log("janky done")).then(() =>
clearTimeout(handle));

perhaps? The doWork function is indeed async, and executed within a
loop that both handles how much work that should be done as well as
using IdleDeadline.timeRemaining as expected.

[1] https://w3c.github.io/requestidlecallback/#invoke-idle-callbacks-algorithm
[2] https://w3c.github.io/requestidlecallback/#the-timeremaining-method

Cheers,
Andreas
_______________________________________________
dev-platform mailing list
dev-platform@lists.mozilla.org
https://lists.mozilla.org/listinfo/dev-platform

Reply via email to