UNPKG

2.18 kBJavaScriptView Raw
1"use strict";
2
3// rawAsap provides everything we need except exception management.
4var rawAsap = require("./raw");
5// RawTasks are recycled to reduce GC churn.
6var 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.
9var pendingErrors = [];
10var requestErrorThrow = rawAsap.makeRequestCallFromTimer(throwFirstError);
11
12function 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 */
26module.exports = asap;
27function 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.
40function 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.
46RawTask.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};