resolver.js 3.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153
  1. /**
  2. * Copyright © Magento, Inc. All rights reserved.
  3. * See COPYING.txt for license details.
  4. */
  5. define([
  6. 'underscore',
  7. 'domReady!'
  8. ], function (_) {
  9. 'use strict';
  10. var context = require.s.contexts._,
  11. execCb = context.execCb,
  12. registry = context.registry,
  13. callbacks = [],
  14. retries = 10,
  15. updateDelay = 1,
  16. ready,
  17. update;
  18. /**
  19. * Checks if provided callback already exists in the callbacks list.
  20. *
  21. * @param {Object} callback - Callback object to be checked.
  22. * @returns {Boolean}
  23. */
  24. function isSubscribed(callback) {
  25. return !!_.findWhere(callbacks, callback);
  26. }
  27. /**
  28. * Checks if provided module is rejected during load.
  29. *
  30. * @param {Object} module - Module to be checked.
  31. * @return {Boolean}
  32. */
  33. function isRejected(module) {
  34. return registry[module.id] && (registry[module.id].inited || registry[module.id].error);
  35. }
  36. /**
  37. * Checks if provided module has unresolved dependencies.
  38. *
  39. * @param {Object} module - Module to be checked.
  40. * @returns {Boolean}
  41. */
  42. function isPending(module) {
  43. if (!module.depCount) {
  44. return false;
  45. }
  46. return module.depCount > _.filter(module.depMaps, isRejected).length;
  47. }
  48. /**
  49. * Checks if requirejs's registry object contains pending modules.
  50. *
  51. * @returns {Boolean}
  52. */
  53. function hasPending() {
  54. return _.some(registry, isPending);
  55. }
  56. /**
  57. * Checks if 'resolver' module is in ready
  58. * state and that there are no pending modules.
  59. *
  60. * @returns {Boolean}
  61. */
  62. function isReady() {
  63. return ready && !hasPending();
  64. }
  65. /**
  66. * Invokes provided callback handler.
  67. *
  68. * @param {Object} callback
  69. */
  70. function invoke(callback) {
  71. callback.handler.call(callback.ctx);
  72. }
  73. /**
  74. * Sets 'resolver' module to a ready state
  75. * and invokes pending callbacks.
  76. */
  77. function resolve() {
  78. ready = true;
  79. callbacks.splice(0).forEach(invoke);
  80. }
  81. /**
  82. * Drops 'ready' flag and runs the update process.
  83. */
  84. function tick() {
  85. ready = false;
  86. update(retries);
  87. }
  88. /**
  89. * Adds callback which will be invoked
  90. * when all of the pending modules are initiated.
  91. *
  92. * @param {Function} handler - 'Ready' event handler function.
  93. * @param {Object} [ctx] - Optional context with which handler
  94. * will be invoked.
  95. */
  96. function subscribe(handler, ctx) {
  97. var callback = {
  98. handler: handler,
  99. ctx: ctx
  100. };
  101. if (!isSubscribed(callback)) {
  102. callbacks.push(callback);
  103. if (isReady()) {
  104. _.defer(tick);
  105. }
  106. }
  107. }
  108. /**
  109. * Checks for all modules to be initiated
  110. * and invokes pending callbacks if it's so.
  111. *
  112. * @param {Number} [retry] - Number of retries
  113. * that will be used to repeat the 'update' function
  114. * invokation in case if there are no pending requests.
  115. */
  116. update = _.debounce(function (retry) {
  117. if (!hasPending()) {
  118. retry ? update(--retry) : resolve();
  119. }
  120. }, updateDelay);
  121. /**
  122. * Overrides requirejs's original 'execCb' method
  123. * in order to track pending modules.
  124. *
  125. * @returns {*} Result of original method call.
  126. */
  127. context.execCb = function () {
  128. var exported = execCb.apply(context, arguments);
  129. tick();
  130. return exported;
  131. };
  132. return subscribe;
  133. });