asap.js 3.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120
  1. let len = 0;
  2. let vertxNext;
  3. let customSchedulerFn;
  4. export var asap = function asap(callback, arg) {
  5. queue[len] = callback;
  6. queue[len + 1] = arg;
  7. len += 2;
  8. if (len === 2) {
  9. // If len is 2, that means that we need to schedule an async flush.
  10. // If additional callbacks are queued before the queue is flushed, they
  11. // will be processed by this flush that we are scheduling.
  12. if (customSchedulerFn) {
  13. customSchedulerFn(flush);
  14. } else {
  15. scheduleFlush();
  16. }
  17. }
  18. }
  19. export function setScheduler(scheduleFn) {
  20. customSchedulerFn = scheduleFn;
  21. }
  22. export function setAsap(asapFn) {
  23. asap = asapFn;
  24. }
  25. const browserWindow = (typeof window !== 'undefined') ? window : undefined;
  26. const browserGlobal = browserWindow || {};
  27. const BrowserMutationObserver = browserGlobal.MutationObserver || browserGlobal.WebKitMutationObserver;
  28. const isNode = typeof self === 'undefined' && typeof process !== 'undefined' && {}.toString.call(process) === '[object process]';
  29. // test for web worker but not in IE10
  30. const isWorker = typeof Uint8ClampedArray !== 'undefined' &&
  31. typeof importScripts !== 'undefined' &&
  32. typeof MessageChannel !== 'undefined';
  33. // node
  34. function useNextTick() {
  35. // node version 0.10.x displays a deprecation warning when nextTick is used recursively
  36. // see https://github.com/cujojs/when/issues/410 for details
  37. return () => process.nextTick(flush);
  38. }
  39. // vertx
  40. function useVertxTimer() {
  41. if (typeof vertxNext !== 'undefined') {
  42. return function() {
  43. vertxNext(flush);
  44. };
  45. }
  46. return useSetTimeout();
  47. }
  48. function useMutationObserver() {
  49. let iterations = 0;
  50. const observer = new BrowserMutationObserver(flush);
  51. const node = document.createTextNode('');
  52. observer.observe(node, { characterData: true });
  53. return () => {
  54. node.data = (iterations = ++iterations % 2);
  55. };
  56. }
  57. // web worker
  58. function useMessageChannel() {
  59. const channel = new MessageChannel();
  60. channel.port1.onmessage = flush;
  61. return () => channel.port2.postMessage(0);
  62. }
  63. function useSetTimeout() {
  64. // Store setTimeout reference so es6-promise will be unaffected by
  65. // other code modifying setTimeout (like sinon.useFakeTimers())
  66. const globalSetTimeout = setTimeout;
  67. return () => globalSetTimeout(flush, 1);
  68. }
  69. const queue = new Array(1000);
  70. function flush() {
  71. for (let i = 0; i < len; i+=2) {
  72. let callback = queue[i];
  73. let arg = queue[i+1];
  74. callback(arg);
  75. queue[i] = undefined;
  76. queue[i+1] = undefined;
  77. }
  78. len = 0;
  79. }
  80. function attemptVertx() {
  81. try {
  82. const vertx = Function('return this')().require('vertx');
  83. vertxNext = vertx.runOnLoop || vertx.runOnContext;
  84. return useVertxTimer();
  85. } catch(e) {
  86. return useSetTimeout();
  87. }
  88. }
  89. let scheduleFlush;
  90. // Decide what async method to use to triggering processing of queued callbacks:
  91. if (isNode) {
  92. scheduleFlush = useNextTick();
  93. } else if (BrowserMutationObserver) {
  94. scheduleFlush = useMutationObserver();
  95. } else if (isWorker) {
  96. scheduleFlush = useMessageChannel();
  97. } else if (browserWindow === undefined && typeof require === 'function') {
  98. scheduleFlush = attemptVertx();
  99. } else {
  100. scheduleFlush = useSetTimeout();
  101. }