1 | ;
|
2 |
|
3 | // rawAsap provides everything we need except exception management.
|
4 | var rawAsap = require("./raw");
|
5 | // RawTasks are recycled to reduce GC churn.
|
6 | var freeTasks = [];
|
7 | // We queue errors to ensure they are thrown in right order (FIFO).
|
8 | // Array-as-queue is good enough here, since we are just dealing with exceptions.
|
9 | var pendingErrors = [];
|
10 | var requestErrorThrow = rawAsap.makeRequestCallFromTimer(throwFirstError);
|
11 |
|
12 | function throwFirstError() {
|
13 | if (pendingErrors.length) {
|
14 | throw pendingErrors.shift();
|
15 | }
|
16 | }
|
17 |
|
18 | /**
|
19 | * Calls a task as soon as possible after returning, in its own event, with priority
|
20 | * over other events like animation, reflow, and repaint. An error thrown from an
|
21 | * event will not interrupt, nor even substantially slow down the processing of
|
22 | * other events, but will be rather postponed to a lower priority event.
|
23 | * @param {{call}} task A callable object, typically a function that takes no
|
24 | * arguments.
|
25 | */
|
26 | module.exports = asap;
|
27 | function asap(task) {
|
28 | var rawTask;
|
29 | if (freeTasks.length) {
|
30 | rawTask = freeTasks.pop();
|
31 | } else {
|
32 | rawTask = new RawTask();
|
33 | }
|
34 | rawTask.task = task;
|
35 | rawAsap(rawTask);
|
36 | }
|
37 |
|
38 | // We wrap tasks with recyclable task objects. A task object implements
|
39 | // `call`, just like a function.
|
40 | function RawTask() {
|
41 | this.task = null;
|
42 | }
|
43 |
|
44 | // The sole purpose of wrapping the task is to catch the exception and recycle
|
45 | // the task object after its single use.
|
46 | RawTask.prototype.call = function () {
|
47 | try {
|
48 | this.task.call();
|
49 | } catch (error) {
|
50 | if (asap.onerror) {
|
51 | // This hook exists purely for testing purposes.
|
52 | // Its name will be periodically randomized to break any code that
|
53 | // depends on its existence.
|
54 | asap.onerror(error);
|
55 | } else {
|
56 | // In a web browser, exceptions are not fatal. However, to avoid
|
57 | // slowing down the queue of pending tasks, we rethrow the error in a
|
58 | // lower priority turn.
|
59 | pendingErrors.push(error);
|
60 | requestErrorThrow();
|
61 | }
|
62 | } finally {
|
63 | this.task = null;
|
64 | freeTasks[freeTasks.length] = this;
|
65 | }
|
66 | };
|