| [9af201e] | 1 | "use strict";
|
|---|
| 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 | };
|
|---|