forked from w3c/requestidlecallback
-
Notifications
You must be signed in to change notification settings - Fork 0
/
index.html
635 lines (630 loc) · 32.8 KB
/
index.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Cooperative Scheduling of Background Tasks</title>
<script src='//www.w3.org/Tools/respec/respec-w3c-common' async class=
'remove'>
</script>
<script class='remove'>
var respecConfig = {
shortName: "requestidlecallback",
specStatus: "ED",
useExperimentalStyles: true,
edDraftURI: "https://w3c.github.io/requestidlecallback/",
editors: [{
name: "Ross McIlroy",
mailto: "[email protected]",
company: "Google Inc.",
companyURL: "https://google.com/",
w3cid: "80940"
}, {
name: "Ilya Grigorik",
url: "https://www.igvita.com/",
mailto: "[email protected]",
company: "Google Inc.",
companyURL: "https://google.com/",
w3cid: "56102"
}],
wg: "Web Performance Working Group",
wgURI: "https://www.w3.org/webperf/",
license: 'w3c-software-doc',
wgPublicList: "public-web-perf",
subjectPrefix: "[RequestIdleCallback]",
format: "markdown",
otherLinks: [{
key: 'Repository',
data: [{
value: 'We are on GitHub.',
href: 'https://github.com/w3c/requestidlecallback/'
}, {
value: 'File a bug.',
href: 'https://github.com/w3c/requestidlecallback/issues'
}, {
value: 'Commit history.',
href: 'https://github.com/w3c/requestidlecallback/commits/gh-pages/index.html'
}, {
value: 'Tests.',
href: 'https://w3c-test.org/requestidlecallback/'
}, {
value: 'Implementation Report.',
href: 'http://wpt.fyi/requestidlecallback'
}]
}],
localBiblio: {
"RESPONSETIME": {
title: "Response time in man-computer conversational transactions",
href: "http://yusufarslan.net/sites/yusufarslan.net/files/upload/content/Miller1968.pdf",
authors: ["Robert B. Miller"],
status: "Fall Joint Computer Conference",
date: "December 1968"
}
},
wgPatentURI: "https://www.w3.org/2004/01/pp-impl/45211/status"
};
</script>
</head>
<body>
<section id="abstract">
<p>This document defines an API that web page authors can use to
cooperatively schedule background tasks such that they do not introduce
delays to other high priority tasks that share the same event loop, such as
input processing, animations and frame compositing. The user agent is in a
better position to determine when background tasks can be run without
introducing user-perceptible delays or jank in animations and input
response, based on its knowledge of currently scheduled tasks, vsync
deadlines, user-interaction and so on. Using this API should therefore
result in more appropriate scheduling of background tasks during times when
the browser would otherwise be idle.</p>
</section>
<section id="sotd">
<p>The Web Performance Working Group maintains a <a href="https://w3c-test.org/requestidlecallback/">test suite</a> for the specification. Please see the Working Group's <a href="http://wpt.fyi/requestidlecallback">implementation report</a>. Vendors interested in implementing this document SHOULD join the mailing lists below and take part in the discussions.</p>
</section>
<section>
<h2>Dependencies</h2>
<p>The terms <dfn data-cite="html#browsing-context">browsing context
</dfn>, <dfn data-cite="html#event-loop">event loop</dfn>,
<dfn data-cite="html#event-loop-processing-model">event loop processing
model</dfn>, <dfn data-cite="html#spin-the-event-loop">spin the event
loop</dfn>, <dfn data-cite="html#fully-active">fully active</dfn>,
<dfn data-cite="html#concept-task">tasks</dfn>,
<dfn data-cite="html#task-source">task source</dfn>,
<dfn data-cite="html#task-queue">task queues</dfn>,
<dfn data-cite="html#queue-a-task">queue a task</dfn>,
<dfn data-cite="html#microtask-queue">microtask queue</dfn>,
<dfn data-cite="html#list-of-active-timers">list of active timers</dfn>,
<code><dfn data-cite="html#dom-settimeout">setTimeout</dfn></code>,
<code><dfn data-cite="html#dom-setinterval">setInterval</dfn></code>,
<code><dfn data-cite="html#dom-window-requestanimationframe">requestAnimationFrame</dfn></code> and
<dfn data-cite="html#report-the-error">report the error</dfn> are defined
in [[!html]].</p>
<p>The terms <dfn data-cite="hr-time-2#dfn-current-high-resolution-time">
current high resolution time</dfn> and
<dfn data-cite="hr-time-2#dom-domhighrestimestamp">DOMHighResTimestamp</dfn>
are defined in [[!hr-time-2]].</p>
<p>The term <dfn data-cite="WebIDL#dfn-conforming-implementation">
conforming implementation</dfn> is defined in [[!WebIDL]].</p>
<p>The term <dfn data-cite="page-visibility#dom-document-hidden">
hidden</dfn> is defined in [[!page-visibility]].</p>
</section>
<section class="informative">
<h2>Introduction</h2>
<p>Web pages often want to execute computation tasks on the browser's event
loop which are not time-critical, but might take a significant portion of
time to perform. Examples of such background tasks include recording
analytics data, long running data processing operations, client-side
templating and pre-rendering of content likely to become visible in the
near future. These tasks must share the event loop with other time-critical
operations, such as reacting to input and performing script-based
animations using <a>requestAnimationFrame</a>. These
background tasks are typically performed by scheduling a callback using
<a>setTimeout</a> and running the background task during that
callback.</p>
<p>A disadvantage of this approach is that the author of the script has no
way to inform the user-agent as to whether a given <code>setTimeout</code> callback is
time-critical or could be delayed until the browser is otherwise idle. In
addition, the user agent isn't able to provide the callback with any
information about how long it can continue to execute without delaying
time-critical operations and causing jank or other user-perceptible delays.
As a result, the easiest way forward is for the author is to simply call
<code>setTimeout</code> with a very small value, and then execute the minimum possible
chunk of work in the resulting callback and reschedule additional work with
another call to <code>setTimeout</code>. This is less than optimal because there is
extra overhead from having to post many small tasks on the user agent's
event loop and schedule their execution. It also relies on the user-agent
interleaving other time-critical work between each of these callbacks
appropriately, which is difficult since the user-agent can't make accurate
assumptions on how long each of these callbacks are likely to take.</p>
<p>The API described in this document allows script authors to request the
user-agent to schedule a callback when it would otherwise be idle. The user
agent provides an estimation of how long it expects to remain idle as a
deadline passed to the callback. The page author can use the deadline to
ensure that these background tasks don't impact latency-critical events
such as animation and input response.</p>
<p>Here is an example of using the API to write a background task.</p>
<pre class='example highlight'>
<!DOCTYPE html>
<title>Scheduling background tasks using requestIdleCallback</title>
<script>
var requestId = 0;
var pointsTotal = 0;
var pointsInside = 0;
function piStep() {
var r = 10;
var x = Math.random() `*` r `*` 2 - r;
var y = Math.random() `*` r `*` 2 - r;
return (Math.pow(x, 2) + Math.pow(y, 2) < Math.pow(r, 2))
}
function refinePi(deadline) {
while (deadline.timeRemaining() > 0) {
if (piStep())
pointsInside++;
pointsTotal++;
}
currentEstimate = (4 `*` pointsInside / pointsTotal);
textElement = document.getElementById("piEstimate");
textElement.innerHTML="Pi Estimate: " + currentEstimate;
requestId = window.requestIdleCallback(refinePi);
}
function start() {
requestId = window.requestIdleCallback(refinePi);
}
function stop() {
if (requestId)
window.cancelIdleCallback(requestId);
requestId = 0;
}
</script>
<button onclick="start()">Click me to start!</button>
<button onclick="stop()">Click me to stop!</button>
<div id="piEstimate">Not started</div>
</pre>
</section>
<section class='informative'>
<h2>Idle Periods</h2>
<p>After input processing, rendering and compositing for a given frame has
been completed, the user agent's main thread often becomes idle until
either: the next frame begins; another pending task becomes eligible to
run; or user input is received. This specification provides a means to
schedule execution of callbacks during this otherwise idle time via a
<code>requestIdleCallback</code> API.</p>
<p>Callbacks posted via the <code>requestIdleCallback</code> API become eligible to
run during user agent defined idle periods. When an idle callback is run it
will be given a deadline which corresponds to the end of the current idle
period. The decision as to what constitutes an idle period is user agent
defined, however the expectation is that they occur in periods of
quiescence where the browser expects to be idle.</p>
<p>One example of an idle period is the time between committing a given
frame to the screen and starting processing on the next frame during active
animations, as shown in <a href="#figure1"></a>. Such idle periods will
occur frequently during active animations and screen updates, but will
typically be very short (i.e., less than 16ms for devices with a 60Hz vsync
cycle).</p>
<!-- Image source avaliable at https://docs.google.com/document/d/1zGgKAJVTDVgq0LpZDL3HD0jNiveivHb_V2Wb8C3jsEE/edit -->
<figure id='figure1'>
<img alt="Example of an inter-frame idle period." src=
"images/image01.png">
<figcaption>
Example of an inter-frame idle period
</figcaption>
</figure>
<p class="note">The web-developer should be careful to account for all work
performed by operations during an idle callback. Some operations, such as
resolving a promise or triggering a page layout, may cause subsequent tasks
to be scheduled to run after the idle callback has finished. In such cases,
the application should account for this additional work by yielding before
the deadline expires to allow these operations to be performed before the
next frame deadline.</p>
<p>Another example of an idle period is when the user agent is idle with no
screen updates occurring. In such a situation the user agent may have no
upcoming tasks with which it can bound the end of the idle period. In order
to avoid causing user-perceptible delays in unpredictable tasks, such as
processing of user input, the length of these idle periods should be capped
to a maximum value of <a href="#why50">50ms</a>. Once an idle period is
finished the user agent can schedule another idle period if it remains
idle, as shown in <a href='#figure2'></a>, to enable background work to
continue to occur over longer idle time periods.</p>
<!-- Image source avaliable at https://docs.google.com/document/d/1zGgKAJVTDVgq0LpZDL3HD0jNiveivHb_V2Wb8C3jsEE/edit -->
<figure id='figure2'>
<img alt=
"Example of an idle period when there are no pending frame updates." src=
"images/image00.png">
<figcaption>
Example of an idle period when there are no pending frame updates
</figcaption>
</figure>
<p>During an idle period the user agent will run idle callbacks in FIFO
order until either the idle period ends or there are no more idle callbacks
eligible to be run. As such, the user agent will not necessarily run all
currently posted idle callbacks within a single idle period. Any remaining
idle tasks are eligible to run during the next idle period.</p>
<p class="note">To deliver the best performance developers are encouraged
to eliminate unnecessary callbacks (e.g. requestAnimationFrame, setTimeout,
and so on) that do not perform meaningful work; do not keep such callbacks
firing and waiting for events to react, instead schedule them as necessary
to react to events once they become available. Such pattern improves
overall efficiency, and enables the user agent to schedule long idle
callbacks (up to 50ms) that can be used to efficiently perform large blocks
of background work.</p>
<p>Only idle tasks which posted before the start of the current idle period
are eligible to be run during the current idle period. As a result, if an
idle callback posts another callback using <code>requestIdleCallback</code>, this
subsequent callback won't be run during the current idle period. This
enables idle callbacks to re-post themselves to be run in a future idle
period if they cannot complete their work by a given deadline - i.e.,
allowing code patterns like the following example, without causing the
callback to be repeatedly executed during a too-short idle period:</p>
<pre class='example highlight'>
function doWork(deadline) {
if (deadline.timeRemaining() <= 5) {
// This will take more than 5ms so wait until we
// get called back with a long enough deadline.
requestIdleCallback(doWork);
return;
}
// do work...
}
</pre>
<p>At the start of the next idle period newly posted idle callbacks are
appended to the end of the runnable idle callback list, thus ensuring that
reposting callbacks will be run round-robin style, with each callback
getting a chance to be run before that of an earlier task's reposted
callback.</p>
<p class="note">Future versions of this specification could allow other
scheduling strategies. For example, schedule idle callback within the same
idle period, a period that has at least <var>X</var> milliseconds of idle
time, and so on. Current specification only supports the case for
scheduling into the next idle period, at which time the callback can
execute its logic, or repost itself into the next idle period.</p>
<p>When the user agent determines that the web page is not user visible it
can throttle idle periods to reduce the power usage of the device, for
example, only triggering an idle period every 10 seconds rather than
continuously.</p>
<p>A final subtlety to note is that there is no guarantee that a user agent
will have any idle CPU time available during heavy page load. As such, it
is entirely acceptable that the user agent does not schedule any idle
period, which would result in the idle callbacks posted via the
<code>requestIdleCallback</code> API being postponed for a potentially unbounded
amount of time. For cases where the author prefers to execute the callback
within an idle period, but requires a time bound within which it can be
executed, the author can provide the <code>timeout</code> property in the <code>options</code>
argument to <code>requestIdleCallback</code>: if the specified timeout is reached
before the callback is executed within an idle period, a task is queued to
execute it.</p>
<p class="note" id='why50'>The maximum deadline of 50ms is derived from
studies [[!RESPONSETIME]] which show that that a response to user input
within 100ms is generally perceived as instantaneous to humans. Capping
idle deadlines to 50ms means that even if the user input occurs immediately
after the idle task has begun, the user agent still has a remaining 50ms in
which to respond to the user input without producing user perceptible
lag.</p>
</section>
<section id="conformance">
<p>The IDL fragments in this specification MUST be interpreted as required
for conforming IDL fragments.</p>
<p>This specification defines a single conformance class:</p>
<dl>
<dt>conforming user agent</dt>
<dd>
A user agent is considered to be a conforming user agent if it
satisfies all of the MUST-, REQUIRED- and SHALL-level criteria in this
specification. A conforming user agent must also be a <a>conforming
implementation</a> of the IDL fragment in section <a href=
"#window_extensions"></a>.
</dd>
</dl>
</section>
<section id='window_extensions'>
<h2><code>Window</code> interface extensions</h2>
<p>The partial interface in the IDL fragment below is used to expose the
<code>requestIdleCallback</code> operation on the
<code><dfn data-cite="HTML5#the-window-object">Window</dfn></code> object.
[[!HTML5]]</p>
<pre class='idl'>
partial interface Window {
unsigned long requestIdleCallback(IdleRequestCallback callback, optional IdleRequestOptions options = {});
void cancelIdleCallback(unsigned long handle);
};
dictionary IdleRequestOptions {
unsigned long timeout;
};
[Exposed=Window] interface IdleDeadline {
DOMHighResTimeStamp timeRemaining();
readonly attribute boolean didTimeout;
};
callback IdleRequestCallback = void (IdleDeadline deadline);
</pre>
<p>Each <a>Window</a> has:</p>
<ul>
<li>A <dfn>list of idle request callbacks</dfn>. The list MUST be
initially empty and each entry in this list is identified by a number,
which MUST be unique within the list for the lifetime of the <code>Window</code>
object.</li>
<li>A <dfn>list of runnable idle callbacks</dfn>. The list MUST be
initially empty and each entry in this list is identified by a number,
which MUST be unique within the list of the lifetime of the <code>Window</code>
object.</li>
<li>An <dfn>idle callback identifier</dfn>, which is a number which MUST
initially be zero.</li>
<li>A <dfn>last idle period deadline</dfn>, which is a
<a>DOMHighResTimeStamp</a> which MUST initially be zero.
</ul>
<section data-link-for="Window">
<h2>The <code><dfn data-dfn-for="Window">requestIdleCallback</dfn></code>
method</h2>
<p>When <a>requestIdleCallback</a><code>(callback, options)</code> is
invoked with a given <code><dfn>IdleRequestCallback</dfn></code>
and optional <code><dfn>IdleRequestOptions</dfn></code>, the user agent
MUST run the following steps:</p>
<ol>
<li>Let <var>window</var> be this <code>Window</code> object.</li>
<li>Increment the <var>window</var>'s <a>idle callback identifier</a>
by one.</li>
<li>Let <var>handle</var> be the current value of <var>window</var>'s
<a>idle callback identifier</a>.</li>
<li>Let <var>start_idle_period</var> be true if the <var>window</var>'s
<a>list of idle request callbacks</a> and it's <a>list of runnable idle
callbacks</a> are empty, otherwise false.</li>
<li>Push <var>callback</var> to the end of <var>window</var>'s <a>list
of idle request callbacks</a>, associated with <var>handle</var>.</li>
<li>If <var>start_idle_period</var> is true:
<ol>
<li><a>queue a task</a> on the queue associated with the idle-task
<a>task source</a>, which performs the <a>start an idle period
algorithm</a>, passing <var>window</var> as a parameter.</li>
</ol>
</li>
<li>Return <var>handle</var> and then continue running this algorithm
asynchronously.
<p class="note">The following steps run in parallel and queue a timer
similar to <code>setTimeout()</code> if the optional <code>timeout</code> property is
provided. From here, the idle and timeout callbacks are raced and
cancel each other—e.g. if the idle callback is scheduled first, then
it cancels the timeout callback, and vice versa.</p>
</li>
<li>If the <code><dfn data-dfn-for="IdleRequestOptions">timeout</dfn>
</code> property is present in <var>options</var> and has a positive
value:
<ol>
<li>Wait for <var>timeout</var> milliseconds.</li>
<li>Wait until all invocations of this algorithm, whose
<var>timeout</var> added to their posted time occurred before this
one's, have completed.</li>
<li>Optionally, wait a further user-agent defined length of time.
<p class='note'>This is intended to allow user agents to pad
timeouts as needed to optimise the power usage of the device. For
example, some processors have a low-power mode where the
granularity of timers is reduced; on such platforms, user agents
can slow timers down to fit this schedule instead of requiring
the processor to use the more accurate mode with its associated
higher power usage.</p>
</li>
<li><a>Queue a task</a> on the queue associated with the idle-task
<a>task source</a>, which performs the <a>invoke idle callback
timeout algorithm</a>, passing <var>handle</var> and
<var>window</var> as arguments.
</li>
</ol>
</li>
</ol>
<p class="note"><a>requestIdleCallback</a>
only schedules a single callback, which will be executed during a
single <a>idle period</a>. If the callback cannot complete its work before
the given <var>deadline</var> then it should call
<a>requestIdleCallback</a> again
(which may be done from within the callback) to schedule a future callback
for the continuation of its task, and exit immediately to return control
back to the <a>event loop</a>.</p>
</section>
<section data-link-for="Window">
<h2>The <code><dfn data-dfn-for="Window">cancelIdleCallback</dfn></code>
method</h2>
<p>The <a>cancelIdleCallback</a> method is used to cancel a
previously made request to schedule an idle callback. When
<a>cancelIdleCallback</a><code>(handle)</code> is invoked, the user agent
MUST run the following steps:
</p>
<ol>
<li>Let <var>window</var> be this <code>Window</code> object.</li>
<li>Find the entry in either the <var>window</var>'s <a>list of idle
request callbacks</a> or <a>list of runnable idle callbacks</a> that is
associated with the value <var>handle</var>.
</li>
<li>If there is such an entry, remove it from both <var>window</var>'s
<a>list of idle request callbacks</a> and the <a>list of runnable idle
callbacks</a>.
</li>
</ol>
<p class='note'><a>cancelIdleCallback</a>
might be invoked for an entry in <var>window</var>'s <a>list of
idle request callbacks</a> or the <a>list of runnable idle callbacks</a>.
In either case the entry should be removed from the list so that the
callback does not run.</p>
</section>
<section data-dfn-for="IdleDeadline">
<h2>The <code><dfn>IdleDeadline</dfn></code> interface</h2>
<p>Each <a>IdleDeadline</a> has an associated <dfn>time</dfn>
which holds a <a>DOMHighResTimeStamp</a> representing the absolute time in
milliseconds of the deadline. This MUST be populated when the
<a>IdleDeadline</a> is created.</p>
<p>When the <code><dfn>timeRemaining</dfn>()</code> method is invoked on
an <a>IdleDeadline</a> object it MUST return the remaining duration before
the deadline expires as a <a>DOMHighResTimeStamp</a>, which SHOULD be
enough to allow measurement while preventing timing attack - see
"Privacy and Security" section of [[HR-TIME]]. This value is calculated
by performing the following steps:</p>
<ol>
<li>Let <var>now</var> be a <a>DOMHighResTimeStamp</a> representing
<a>current high resolution time</a> in milliseconds.</li>
<li>Let <var>deadline</var> be the <a>time</a> associated with the
<a>IdleDeadline</a> object.</li>
<li>Let <var>timeRemaining</var> be <var>deadline</var> -
<var>now</var>.</li>
<li>If the user agent now has a time-critical task pending,
set <var>timeRemaining</var> to 0.</li>
<li>If <var>timeRemaining</var> is negative, set it to 0.</li>
<li>Return <var>timeRemaining</var>.</li>
</ol>
<p>Each <a>IdleDeadline</a> has an associated <dfn>timeout</dfn>, which is
initially false. The <code><dfn>didTimeout</dfn></code> getter MUST
return <a>timeout</a>.</p>
<p class='note'>The <a>invoke idle callback timeout algorithm</a> sets
<a>timeout</a> to true to specify that the callback is being executed
outside an idle period, due to it having exceeded the value of the
<a>IdleRequestOptions</a>'s timeout property which was passed when the
callback was registered.</p>
</section>
</section>
<section>
<h2>Processing</h2>
<section>
<h2>Start an idle period algorithm</h2>
<p>The <dfn>start an idle period algorithm</dfn>, which is called
by the <a>event loop processing model</a> when it determines that
the <a>event loop</a> is otherwise idle:</p>
<ol>
<li>Let <var>last_deadline</var> be the <a>last idle period deadline</a>
associated with <var>window</var>
<li>If <var>last_deadline</var> is greater than the current time,
return from this algorithm.
<li>Optionally, if the user agent determines the idle period should
be delayed, return from this algorithm.
<p class='note'>This is intended to allow user agents to delay the
start of idle periods as needed to optimise the power usage of the
device. For example, if the <code>Document</code>'s <a>hidden</a>
attribute ([[!page-visibility]]) is <code>true</code> then the user agent can
throttle idle period generation, for example limiting the Document to
one idle period every 10 seconds to optimize for power usage.</p>
</li>
<li>Let <var>now</var> be the current time.</li>
<li>Let <var>deadline</var> be a time in the future until which the
browser expects to remain idle. The user agent SHOULD choose
<var>deadline</var> to ensure that no time-critical tasks will be
delayed even if a callback runs for the whole time period from
<var>now</var> to <var>deadline</var>. As such, it should be set to the
minimum of: the closest timeout in the <a>list of active timers</a> as
set via <a>setTimeout</a> and <a>setInterval</a>; the scheduled
runtime for pending animation callbacks posted via
<a>requestAnimationFrame</a>; pending internal timeouts such
as deadlines to start rendering the next frame, process audio or any
other internal task the user agent deems important.</li>
<li>If <var>deadline</var> - <var>now</var> is greater than 50ms, then
cap <var>deadline</var> by setting it to be <var>now</var> + 50ms.
<p class='note'>The cap of 50ms in the future is to ensure
responsiveness to new user input within the threshold of human
perception.</p>
</li>
<li>Let <var>pending_list</var> be <var>window</var>'s <a>list of idle
request callbacks</a>.
</li>
<li>Let <var>run_list</var> be <var>window</var>'s <a>list of runnable
idle callbacks</a>.
</li>
<li>Append all entries from <var>pending_list</var> into
<var>run_list</var> preserving order.</li>
<li>Clear <var>pending_list</var>.</li>
<li><a>Queue a task</a> on the queue associated with the idle-task
<a>task source</a>, which performs the steps defined in the <a>invoke
idle callbacks algorithm</a> with <var>deadline</var> and
<var>window</var> as parameters.
</li>
<li>Save <var>deadline</var> as the <a>last idle period deadline</a>
associated with <var>window</var>.</li>
</ol>
<p>The <a>task source</a> for these <a>tasks</a> is the <dfn>idle-task
task source</dfn>.</p>
<div class="note">
<p>The time between <var>now</var> and <var>deadline</var> is referred
to as the <dfn>idle period</dfn>. There can only be one idle period
active at a given time for any given <code>window</code>. The idle period can end
early if the user agent determines that it is no longer idle. If so,
the next idle period cannot start until after <var>deadline</var>.</p>
</div>
</section>
<section>
<h2>Invoke idle callbacks algorithm</h2>
<p>The <dfn>invoke idle callbacks algorithm</dfn>:</p>
<ol>
<li>If the user-agent believes it should end the idle period early due
to newly scheduled high-priority work, skip to step 4.
<li>Let <var>now</var> be the current time.</li>
<li>If <var>now</var> is less than <var>deadline</var>:
<ol>
<li>Pop the top <var>callback</var> from <var>window</var>'s
<a>list of runnable idle callbacks</a>.</li>
<li>Let <var>deadlineArg</var> be a new <a>IdleDeadline</a>.
Set the <a>time</a> associated with <var>deadlineArg</var> to
<var>deadline</var> and set the <a>timeout</a> associated with
<var>deadlineArg</var> to <code>false</code>.</li>
<li>Call <var>callback</var> with <var>deadlineArg</var> as its
argument. If an uncaught runtime script error occurs, then <a>report
the error</a>.</li>
<li>If <var>window</var>'s <a>list of runnable idle callbacks</a>
is not empty, <a>queue a task</a> which performs the steps in the
<a>invoke idle callbacks algorithm</a> with <var>deadline</var>
and <var>window</var> as a parameters and return from this
algorithm</li>
</ol>
</li>
<li>Otherwise, if either the <var>window</var>'s <a>list of idle request
callbacks</a> or it's <a>list of runnable idle callbacks</a> are
not empty, <a>queue a task</a> which performs the steps in the <a>start
an idle period algorithm</a> algorithm with <var>window</var> and as a
parameter.</li>
</ol>
<p class="note">The user agent is free to end an idle period early, even
if <var>deadline</var> has not yet occurred, by deciding to skip from step
1 directly to step 4. For example, the user agent may decide to do
this if it determines that higher priority work has become runnable.</p>
</section>
<section>
<h2>Invoke idle callback timeout algorithm</h2>
<p>The <dfn>invoke idle callback timeout algorithm</dfn>:</p>
<ol>
<li>Let <var>callback</var> be the result of finding the entry in <var>
window</var>'s <a>list of idle request callbacks</a> or the <a>list
of runnable idle callbacks</a> that is associated with the value
given by the <var>handle</var> argument passed to the algorithm.
</li>
<li>If <var>callback</var> is not undefined:
<ol>
<li>Remove <var>callback</var> from both <var>window</var>'s <a>list
of idle request callbacks</a> and the <a>list of runnable idle
callbacks</a>.</var></li>
<li>Let <var>now</var> be the current time.</li>
<li>Let <var>deadlineArg</var> be a new <a>IdleDeadline</a>.
Set the <a>time</a> associated with <var>deadlineArg</var> to
<var>now</var> and set the <a>timeout</a> associated with
<var>deadlineArg</var> to <code>true</code>.</li>
<li>Call <var>callback</var> with <var>deadlineArg</var> as its
argument. If an uncaught runtime script error occurs, then <a>report
the error</a>.</li>
</ol>
</li>
</ol>
</section>
</section>
<section>
<h2>Privacy and Security</h2>
<p>When an idle callback is scheduled the user agent provides an estimate
of how long it expects to remain idle. This information can be used to
estimate the time taken by other application tasks and related browser work
within that frame. However, developers can already access this information
via other means - e.g. mark beginning of the frame via
<code>requestAnimationFrame</code>, estimate the time of the next frame, and use that
information to compute "remaining time" within any callback.</p>
<p>To mitigate cache and statistical fingerprinting attacks, the resolution
of the time estimates returned by the <a>IdleDeadline</a> interface should
be set to the same 5 microsecond minimum as the <code>Performance</code> interface
defined in [[HR-TIME]].</p>
</section>
<section id="acknowledgements" class="appendix">
<h2>Acknowledgments</h2>
<p>The editors would like to thank the following people for contributing to
this specification: Sami Kyostila, Alex Clarke, Boris Zbarsky, Marcos
Caceres, Jonas Sicking, Robert O'Callahan, David Baron, Todd Reifsteck,
Tobin Titus, Elliott Sprehn, Tetsuharu OHZEKI, Lon Ingram, Domenic Denicola,
Philippe Le Hegaret and Anne van Kesteren .</p>
</section>
</body>
</html>