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