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
|
---
title: Background Tasks API
slug: Web/API/Background_Tasks_API
translation_of: Web/API/Background_Tasks_API
---
<div>{{DefaultAPISidebar("Tarefas em segundo plano")}}{{draft}}</div>
<p>The <strong>Cooperative Scheduling of Background Tasks API</strong> (also referred to as the Background Tasks API or simply the <code>requestIdleCallback()</code> API) provides the ability to queue tasks to be executed automatically by the user agent when it determines that there is free time to do so.</p>
<h2 id="Conceitos_e_uso">Conceitos e uso</h2>
<p>The main thread of a Web browser is centered around its event loop. This code draws any pending updates to the {{domxref("Document")}} currently being displayed, runs any JavaScript code the page needs to run, accepts events from input devices, and dispatches those events to the elements that should receive them. In addition, the event loop handles interactions with the operating system, updates to the browser's own user interface, and so forth. It's an extremely busy chunk of code, and your main JavaScript code may run right inside this thread along with all of this. Certainly most if not all code that is capable of making changes to the DOM is running in the main thread, since it's common for user interface changes to only be available to the main thread.</p>
<p>Because event handling and screen updates are two of the most obvious ways users notice performance issues, it's important for your code to be a good citizen of the Web and help to prevent stalls in the execution of the event loop. In the past, there's been no way to do this reliably other than by writing code that's as efficient as possible and by offloading as much work as possible to <a href="/en-US/docs/Web/API/Web_Workers_API">workers</a>. {{domxref("Window.requestIdleCallback()")}} makes it possible to become actively engaged in helping to ensure that the browser's event loop runs smoothly, by allowing the browser to tell your code how much time it can safely use without causing the system to lag. If you stay within the limit given, you can make the user's experience much better.</p>
<h3 id="Getting_the_most_out_of_idle_callbacks">Getting the most out of idle callbacks</h3>
<p>Because idle callbacks are intended to give your code a way to cooperate with the event loop to ensure that the system is utilized to its full potential without over-tasking it, resulting in lag or other performance problems, you should be thoughtful about how you go about using them.</p>
<ul>
<li><strong>Use idle callbacks for tasks which don't have high priority.</strong> Because you don't know how many callbacks have been established, and you don't know how busy the user's system is, you don't know how often your callback will be run (unless you specify a <code>timeout</code>. There's no guarantee that every pass through the event loop (or even every screen update cycle) will include any idle callbacks being executed; if the event loop uses all available time, you're out of luck (again, unless you've used a <code>timeout</code>).</li>
<li><strong>Idle callbacks should do their best not to overrun the time allotted.</strong> While the browser, your code, and the Web in general will continue to run normally if you go over the specified time limit (even if you go<strong> </strong><em>way</em> over it), the time restriction is intended to ensure that you leave the system enough time to finish the current pass through the event loop and get on to the next one without causing other code to stutter or animation effects to lag. Currently, {{domxref("IdleDeadline.timeRemaining", "timeRemaining()")}} has an upper limit of 50 milliseconds, but in reality you will often have less time than that, since the event loop may already be eating into that time on complex sites, with browser extensions needing processor time, and so forth.</li>
<li><strong>Avoid making changes to the DOM within your idle callback.</strong> By the time your callback is run, the current frame has already finished drawing, and all layout updates and computations have been completed. If you make changes that affect layout, you may force a situation in which the browser has to stop and do recalculations that would otherwise be unnecessary. If your callback needs to change the DOM, it should use {{domxref("Window.requestAnimationFrame()")}} to schedule that.</li>
<li><strong>Avoid tasks whose run time can't be predicted.</strong> Your idle callback should avoid doing anything that could take an unpredictable amount of time. For example, anything which might affect layout should be avoided. You should also avoid resolving or rejecting {{domxref("Promise")}}s, since that would invoke the handler for that promise's resolution or rejection as soon as your callback returns.</li>
<li><strong>Use timeouts when you need to, but only when you need to.</strong> Using timeouts can ensure that your code runs in a timely manner, but it can also allow you to cause lag or animation stutters by mandating that the browser call you when there's not enough time left for you to run without disrupting performance.</li>
</ul>
<h3 id="Falling_back_to_setTimeout">Falling back to setTimeout</h3>
<p>Because the Background Tasks API is fairly new, your code may need to be able to work on browsers that don't yet support it. You can do so with a simple shim that uses {{domxref("WindowTimers.setTimeout()", "setTimeout()")}} as a fallback option. This isn't a {{Glossary("polyfill")}}, since it's not functionally identical; <code>setTimeout()</code> doesn't let you make use of idle periods, but instead runs your code when possible, leaving us to do the best we can to avoid causing the user to experience performance lag.</p>
<pre class="brush: js">window.requestIdleCallback = window.requestIdleCallback || function(handler) {
let startTime = Date.now();
return setTimeout(function() {
handler({
didTimeout: false,
timeRemaining: function() {
return Math.max(0, 50.0 - (Date.now() - startTime));
}
});
}, 1);
}</pre>
<p>If {{domxref("Window.requestIdleCallback", "window.requestIdleCallback")}} is undefined, we create it here. The function begins by recording the time at which our implementation was called. We'll be using that to compute the value returned by our shim for {{domxref("IdleDeadline.timeRemaining()", "timeRemaining()")}}.</p>
<p>Then we call {{domxref("WindowTimers.setTimeout", "setTimeout()")}}, passing into it a function which runs the callback passed into our implementation of <code>requestIdleCallback()</code>. The callback is passed an object which conforms to {{domxref("IdleDeadline")}}, with {{domxref("IdleDeadline.didTimeout", "didTimeout")}} set to <code>false</code> and a<code> </code>{{domxref("IdleDeadline.timeRemaining", "timeRemaining()")}} method which is implemented to give the callback 50 milliseconds of time to begin with. Each time <code>timeRemaining()</code> is called, it subtracts the elapsed time from the original 50 milliseconds to determine the amount of time left.</p>
<p>As a result, while our shim doesn't constrain itself to the amount of idle time left in the current event loop pass like the true <code>requestIdleCallback()</code>, it does at least limit the callback to no more than 50 milliseconds of run time per pass.</p>
<p>The implementation of our shim for {{domxref("Window.cancelIdleCallback", "cancelIdleCallback()")}} is much simpler:</p>
<pre class="brush: js">window.cancelIdleCallback = window.cancelIdleCallback || function(id) {
clearTimeout(id);
}</pre>
<p>If <code>cancelIdleCallback()</code> isn't defined, this creates one which simply passes the specified callback ID through to {{domxref("WindowTimers.clearTimeout", "clearTimeout()")}}.</p>
<p>Now your code will work even on browsers that don't support the Background Tasks API, albeit not as efficiently.</p>
<h2 id="Interfaces">Interfaces</h2>
<p>The Background Tasks API adds only one new interface:</p>
<dl>
<dt>{{domxref("IdleDeadline")}}</dt>
<dd>An object of this type is passed to the idle callback to provide an estimate of how long the idle period is expected to last, as well as whether or not the callback is running because its timeout period has expired.</dd>
</dl>
<p>The {{domxref("Window")}} interface is also augmented by this API to offer the new {{domxref("window.requestIdleCallback", "requestIdleCallback()")}} and {{domxref("window.cancelIdleCallback", "cancelIdleCallback()")}} methods.</p>
<h2 id="Example" name="Example">Example</h2>
<p>In this example, we'll take a look at how you can use {{domxref("window.requestIdleCallback", "requestIdleCallback()")}} to run time-consuming, low-priority tasks during time the browser would otherwise be idle. In addition, this example demonstrates how to schedule updates to the document content using {{domxref("window.requestAnimationFrame", "requestAnimationFrame()")}}.</p>
<p>Below you'll find only the HTML and JavaScript for this example. The CSS is not shown, since it's not particularly crucial to understanding this functionality.</p>
<h3 id="HTML_content">HTML content</h3>
<p>In order to be oriented about what we're trying to accomplish, let's have a look at the HTML. This establishes a box (ID <code>"Container"</code>) that's used to present the progress of an operation (because you never know how long decoding "quantum filament tachyon emissions" will take, after all) as well as a second main box (with the ID <code>"logBox"</code>), which is used to display textual output.</p>
<pre class="brush: html"><p>
Demonstration of using <a href="https://developer.mozilla.org/en-US/docs/Web/API/Background_Tasks_API">
cooperatively scheduled background tasks</a> using the <code>requestIdleCallback()</code>
method.
</p>
<div class="container">
<div class="label">Decoding quantum filament tachyon emissions...</div>
<progress id="progress" value="0"></progress>
<div class="button" id="startButton">
Start
</div>
<div class="label counter">
Task <span id="currentTaskNumber">0</span> of <span id="totalTaskCount">0</span>
</div>
</div>
<div class="logBox">
<div class="logHeader">
Log
</div>
<div id="log">
</div>
</div></pre>
<p>The progress box uses a {{HTMLElement("progress")}} element to show the progress, along with a label with sections that are changed to present numeric information about the progress. In addition, there's a "Start" button (creatively given the ID "startButton"), which the user will use to start the data processing.</p>
<div class="hidden">
<h3 id="CSS_content">CSS content</h3>
<pre class="brush: css">body {
font-family: "Open Sans", "Lucida Grande", "Arial", sans-serif;
font-size: 16px;
}
.logBox {
margin-top: 16px;
width: 400px;
height:500px;
border-radius: 6px;
border: 1px solid black;
box-shadow: 4px 4px 2px black;
}
.logHeader {
margin: 0;
padding: 0 6px 4px;
height: 22px;
background-color: lightblue;
border-bottom: 1px solid black;
border-radius: 6px 6px 0 0;
}
#log {
font: 12px "Courier", monospace;
padding: 6px;
overflow: auto;
overflow-y: scroll;
width: 388px;
height: 460px;
}
.container {
width: 400px;
padding: 6px;
border-radius: 6px;
border: 1px solid black;
box-shadow: 4px 4px 2px black;
display: block;
overflow: auto;
}
.label {
display: inline-block;
}
.counter {
text-align: right;
padding-top: 4px;
float: right;
}
.button {
padding-top: 2px;
padding-bottom: 4px;
width: 100px;
display: inline-block;
float: left;
border: 1px solid black;
cursor: pointer;
text-align: center;
margin-top: 0;
color: white;
background-color: darkgreen;
}
#progress {
width: 100%;
padding-top: 6px;
}</pre>
</div>
<h3 id="JavaScript_content">JavaScript content</h3>
<p>Now that the document structure is defined, construct the JavaScript code that will do the work. The goal: to be able to add requests to call functions to a queue, with an idle callback that runs those functions whenever the system is idle for long enough a time to make progress.</p>
<h4 id="Declaração_de_variáveis">Declaração de variáveis</h4>
<pre class="brush: js">let taskList = [];
let totalTaskCount = 0;
let currentTaskNumber = 0;
let taskHandle = null;
</pre>
<p>These variables are used to manage the list of tasks that are waiting to be performed, as well as status information about the task queue and its execution:</p>
<ul>
<li><code>taskList</code> is an {{jsxref("Array")}} of objects, each representing one task waiting to be run.</li>
<li><code>totalTaskCount</code> is a counter of the number of tasks that have been added to the queue; it will only go up, never down. We use this to do the math to present progress as a precentage of total work to do.</li>
<li><code>currentTaskNumber</code> is used to track how many tasks have been processed so far.</li>
<li><code>taskHandle</code> is a reference to the task currently being processed.</li>
</ul>
<pre class="brush: js">let totalTaskCountElem = document.getElementById("totalTaskCount");
let currentTaskNumberElem = document.getElementById("currentTaskNumber");
let progressBarElem = document.getElementById("progress");
let startButtonElem = document.getElementById("startButton");
let logElem = document.getElementById("log");
</pre>
<p>Next we have variables which reference the DOM elements we need to interact with. These elements are:</p>
<ul>
<li><code>totalTaskCountElem</code> is the {{HTMLElement("span")}} we use to insert the total number of tasks created into the status display in the progress box.</li>
<li><code>currentTaskNumberElem</code> is the element used to display the number of tasks processed so far.</li>
<li><code>progressBarElem</code> is the {{HTMLElement("progress")}} element showing the percentage of the tasks processed so far.</li>
<li><code>startButtonElem</code> is the start button.</li>
<li><code>logElem</code> is the {{HTMLElement("div")}} we'll insert logged text messages into.</li>
</ul>
<pre class="brush: js">let logFragment = null;
let statusRefreshScheduled = false;
</pre>
<p>Finally, we set up a couple of variables for other items:</p>
<ul>
<li><code>logFragment</code> will be used to store a {{domxref("DocumentFragment")}} that's generated by our logging functions to create content to append to the log when the next animation frame is rendered.</li>
<li><code>statusRefreshScheduled</code> is used to track whether or not we've already scheduled an update of the status display box for the upcoming frame, so that we only do it once per frame</li>
</ul>
<div class="hidden">
<p>The shim to function even if idle callbacks aren't supported. Already discussed above, so it's hidden here to save space in the article.</p>
<pre class="brush: js">window.requestIdleCallback = window.requestIdleCallback || function(handler) {
let startTime = Date.now();
return setTimeout(function() {
handler({
didTimeout: false,
timeRemaining: function() {
return Math.max(0, 50.0 - (Date.now() - startTime));
}
});
}, 1);
};
window.cancelIdleCallback = window.cancelIdleCallback || function(id) {
clearTimeout(id);
};
</pre>
</div>
<h4 id="Gerenciando_a_lista_de_tarefas">Gerenciando a lista de tarefas</h4>
<p>Next, let's look at the way we manage the tasks that need to be performed. We're going to do this by creating a FIFO queue of tasks, which we'll run as time allows during the idle callback period.</p>
<h5 id="Enqueueing_tasks">Enqueueing tasks</h5>
<p>First, we need a function that enqueues tasks for future execution. That function, <code>enqueueTask()</code>, looks like this:</p>
<pre class="brush: js">function enqueueTask(taskHandler, taskData) {
taskList.push({
handler: taskHandler,
data: taskData
});
totalTaskCount++;
if (!taskHandle) {
taskHandle = requestIdleCallback(runTaskQueue, { timeout: 1000 });
}
scheduleStatusRefresh();
}
</pre>
<p><code>enqueueTask()</code> accepts as input two parameters:</p>
<ul>
<li><code>taskHandler</code> is a function which will be called to handle the task.</li>
<li><code>taskData</code> is an object which is passed into the task handler as an input parameter, to allow the task to receive custom data.</li>
</ul>
<p>To enqueue the task, we <a href="/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/push">push</a> an object onto the <code>taskList</code> array; the object contains the <code>taskHandler</code> and <code>taskData</code> values under the names <code>handler</code> and <code>data</code>, respectively, then increment <code>totalTaskCount</code>, which reflects the total number of tasks which have ever been enqueued (we don't decrement it when tasks are removed from the queue).</p>
<p>Next, we check to see if we already have an idle callback created; if <code>taskHandle</code> is 0, we know there isn't an idle callback yet, so we call {{domxref("Window.requestIdleCallback", "requestIdleCallback()")}} to create one. It's configured to call a function called <code>runTaskQueue()</code>, which we'll look at shortly, and with a <code>timeout</code> of 1 second, so that it will be run at least once per second even if there isn't any actual idle time available.</p>
<h5 id="Running_tasks">Running tasks</h5>
<p>Our idle callback handler, runTaskQueue(), gets called when the browser determines there's enough idle time available to let us do some work or our timeout of one second expires. This function's job is to run our enqueued tasks.</p>
<pre class="brush: js">function runTaskQueue(deadline) {
while ((deadline.timeRemaining() > 0 || deadline.didTimeout) && taskList.length) {
let task = taskList.shift();
currentTaskNumber++;
task.handler(task.data);
scheduleStatusRefresh();
}
if (taskList.length) {
taskHandle = requestIdleCallback(runTaskQueue, { timeout: 1000} );
} else {
taskHandle = 0;
}
}
</pre>
<p><code>runTaskQueue()</code>'s core is a loop which continues as long as there's time left (as determined by checking {{domxref("IdleDeadline.timeRemaining", "deadline.timeRemaining")}}) to be sure it's more than 0 or if the timeout limit was reached ({{domxref("IdleDeadline.didTimeout", "deadline.didTimeout")}} is true), and as long as there are tasks in the task list.</p>
<p>For each task in the queue that we have time to execute, we do the following:</p>
<ol>
<li>We <a href="/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/shift">remove the task object from the queue</a>.</li>
<li>We increment <code>currentTaskNumber</code> to track how many tasks we've executed.</li>
<li>We call the task's handler, <code>task.handler</code>, passing into it the task's data object (<code>task.data</code>).</li>
<li>We call a function, <code>scheduleStatusRefresh()</code>, to handle scheduling a screen update to reflect changes to our progress.</li>
</ol>
<p>When time runs out, if there are still tasks left in the list, we call {{domxref("Window.requestIdleCallback", "requestIdleCallback()")}} again so that we can continue to process the tasks the next time there's idle time available. If the queue is empty, we set taskHandle to 0 to indicate that we don't have a callback scheduled. That way, we'll know to request a callback next time <code>enqueueTask()</code> is called.</p>
<h4 id="Updating_the_status_display">Updating the status display</h4>
<p>One thing we want to be able to do is update our document with log output and progress information. However, you can't safely change the DOM from within an idle callback. Instead, we'll use {{domxref("Window.requestAnimationFrame", "requestAnimationFrame()")}} to ask the browser to call us when it's safe to update the display.</p>
<h5 id="Scheduling_display_updates">Scheduling display updates</h5>
<p>DOM changes are scheduled by calling the <code>scheduleStatusRefresh()</code> function.</p>
<pre class="brush: js">function scheduleStatusRefresh() {
if (!statusRefreshScheduled) {
requestAnimationFrame(updateDisplay);
statusRefreshScheduled = true;
}
}
</pre>
<p>This is a simple function. It checks to see if we've already scheduled a display refresh by checking the value of <code>statusRefreshScheduled</code>. If it's <code>false</code>, we call {{domxref("Window.requestAnimationFrame", "requestAnimationFrame()")}} to schedule a refresh, providing the <code>updateDisplay()</code> function to be called to handle that work.</p>
<h5 id="Updating_the_display">Updating the display</h5>
<p>The <code>updateDisplay()</code> function is responsible for drawing the contents of the progress box and the log. It's called by the browser when the DOM is in a safe condition for us to apply changes during the process of rendering the next frame.</p>
<pre class="brush: js">function updateDisplay() {
let scrolledToEnd = logElem.scrollHeight - logElem.clientHeight <= logElem.scrollTop + 1;
if (totalTaskCount) {
if (progressBarElem.max != totalTaskCount) {
totalTaskCountElem.textContent = totalTaskCount;
progressBarElem.max = totalTaskCount;
}
if (progressBarElem.value != currentTaskNumber) {
currentTaskNumberElem.textContent = currentTaskNumber;
progressBarElem.value = currentTaskNumber;
}
}
if (logFragment) {
logElem.appendChild(logFragment);
logFragment = null;
}
if (scrolledToEnd) {
logElem.scrollTop = logElem.scrollHeight - logElem.clientHeight;
}
statusRefreshScheduled = false;
}</pre>
<p>First, <code>scrolledToEnd</code> is set to <code>true</code> if the text in the log is scrolled to the bottom; otherwise it's set to <code>false</code>. We'll use that to determine if we should update the scroll position to ensure that the log stays at the end when we're done adding content to it.</p>
<p>Next, we update the progress and status information if any tasks have been enqueued.</p>
<ol>
<li>If the current maximum value of the progress bar is different from the current total number of enqueued tasks (<code>totalTaskCount</code>), then we update the contents of the displayed total number of tasks (<code>totalTaskCountElem</code>) and the maximum value of the progress bar, so that it scales properly.</li>
<li>We do the same thing with the number of tasks processed so far; if <code>progressBarElem.value</code> is different from the task number currently being processed (<code>currentTaskNumber</code>), then we update the displayed value of the currently-being-processed task and the current value of the progress bar.</li>
</ol>
<p>Then, if there's text waiting to be added to the log (that is, if <code>logFragment</code> isn't <code>null</code>), we append it to the log element using {{domxref("Node.appendChild", "Element.appendChild()")}} and set <code>logFragment</code> to <code>null</code> so we don't add it again.</p>
<p>If the log was scrolled to the end when we started, we make sure it still is. Then we set <code>statusRefreshScheduled</code> to <code>false</code> to indicate that we've handled the refresh and that it's safe to request a new one.</p>
<h4 id="Adding_text_to_the_log">Adding text to the log</h4>
<p>The <code>log()</code> function adds the specified text to the log. Since we don't know at the time <code>log()</code> is called whether or not it's safe to immediately touch the DOM, we will cache the log text until it's safe to update. Above, in the code for <code>updateDisplay</code><code>()</code>, you can find the code that actually adds the logged text to the log element when the animation frame is being updated.</p>
<pre class="brush: js">function log(text) {
if (!logFragment) {
logFragment = document.createDocumentFragment();
}
let el = document.createElement("div");
el.innerHTML = text;
logFragment.appendChild(el);
}
</pre>
<p>First, we create a {{domxref("DocumentFragment")}} object named <code>logFragment</code> if one doesn't currently exist. This element is a pseudo-DOM into which we can insert elements without immediately changing the main DOM itself.</p>
<p>We then create a new {{HTMLElement("div")}} element and set its contents to match the input <code>text</code>. Then we append the new element to the end of the pseudo-DOM in <code>logFragment</code>. <code>logFragment</code> will accumulate log entries until the next time <code>updateDisplay()</code> is called because the DOM for the changes.</p>
<h3 id="Running_tasks_2">Running tasks</h3>
<p>Now that we've got the task management and display maintenance code done, we can actually start setting up code to run tasks that get work done.</p>
<h4 id="The_task_handler">The task handler</h4>
<p>The function we'll be using as our task handler—that is, the function that will be used as the value of the task object's <code>handler</code> property—is <code>logTaskHandler</code>(). It's a simple function that outputs a bunch of stuff to the log for each task. In your own application, you'd replace this code with whatever task it is you wish to perform during idle time. Just remember that anything you want to do that changes the DOM needs to be handled through {{domxref("Window.requestAnimationFrame", "requestAnimationFrame()")}}.</p>
<pre class="brush: js">function logTaskHandler(data) {
log("<strong>Running task #" + currentTaskNumber + "</strong>");
for (i=0; i<data.count; i+=1) {
log((i+1).toString() + ". " + data.text);
}
}
</pre>
<h4 id="The_main_program">The main program</h4>
<p>Everything is triggered when the user clicks the Start button, which causes the <code>decodeTechnoStuff()</code> function to be called.</p>
<div class="hidden">
<p>The <code><a href="/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/random?document_saved=true#Getting_a_random_integer_between_two_values_inclusive">getRandomIntInclusive()</a></code> method comes from the examples for {{jsxref("Math.random()")}}; we'll just link to it below but it needs to be included here for the example to work.</p>
<pre class="brush: js">function getRandomIntInclusive(min, max) {
min = Math.ceil(min);
max = Math.floor(max);
return Math.floor(Math.random() * (max - min + 1)) + min;
}
</pre>
</div>
<pre class="brush: js">function decodeTechnoStuff() {
totalTaskCount = 0;
currentTaskNumber = 0;
updateDisplay();
let n = getRandomIntInclusive(100, 200);
for (i=0; i<n; i++) {
let taskData = {
count: getRandomIntInclusive(75, 150),
text: "This text is from task number " + (i+1).toString() + " of " + n
};
enqueueTask(logTaskHandler, taskData);
}
}
document.getElementById("startButton").addEventListener("click", decodeTechnoStuff, false);</pre>
<p><code>decodeTechnoStuff()</code> starts by zeroing the values of totalTaskCount (the number of tasks added to the queue so far) and currentTaskNumber (the task currently being run), and then calls <code>updateDisplay()</code> to reset the display to its "nothing's happened yet" state.</p>
<p>This example will create a random number of tasks (between 100 and 200 of them). To do so, we use the <a href="/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/random?document_saved=true#Getting_a_random_integer_between_two_values_inclusive"><code>getRandomIntInclusive()</code> function</a> that's provided as an example in the documentation for {{jsxref("Math.random()")}} to get the number of tasks to create.</p>
<p>Then we start a loop to create the actual tasks. For each task, we create an object, <code>taskData</code>, which includes two properties:</p>
<ul>
<li><code>count</code> is the number of strings to output into the log from the task.</li>
<li><code>text</code> is the text to output to the log the number of times specified by <code>count</code>.</li>
</ul>
<p>Each task is then enqueued by calling <code>enqueueTask()</code>, passing in <code>logTaskHandler</code><code>()</code> as the handler function and the <code>taskData</code> object as the object to pass into the function when it's called.</p>
<dl>
</dl>
<h3 id="Result">Result</h3>
<p>Below is the actual functioning result of the code above. Try it out, play with it in your browser's developer tools, and experiment with using it in your own code.</p>
<p>{{ EmbedLiveSample('Example', 600, 700) }}</p>
<h2 id="Specifications">Specifications</h2>
<table class="standard-table">
<tbody>
<tr>
<th scope="col">Specification</th>
<th scope="col">Status</th>
<th scope="col">Comment</th>
</tr>
<tr>
<td>{{SpecName("Background Tasks")}}</td>
<td>{{Spec2("Background Tasks")}}</td>
<td></td>
</tr>
</tbody>
</table>
<h2 id="Browser_compatibility">Compatibilidade com navegadores</h2>
<div>
<p>{{Compat("api.Window.requestIdleCallback")}}</p>
</div>
<h2 id="See_also">See also</h2>
<ul>
<li>{{domxref("Window.requestIdleCallback()")}}</li>
<li>{{domxref("Window.cancelIdleCallback()")}}</li>
<li>{{domxref("IdleDeadline")}}</li>
</ul>
|