browser-asap.js 2.1 KB

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