Merge pull request #9560 from aydun/CRM-19754
[civicrm-core.git] / ang / crmUtil.js
index efe847cee72d34c6d2485b9e108045d6b9f31dbd..c48b2cbc54ea3e4510ebc8748d498769560ece45 100644 (file)
@@ -16,7 +16,7 @@
       var deferred = $q.defer();
       var p;
       var backend = crmApi.backend || CRM.api3;
-      if (params.body_html) {
+      if (params && params.body_html) {
         // CRM-18474 - remove Unicode Character 'LINE SEPARATOR' (U+2028)
         // and 'PARAGRAPH SEPARATOR' (U+2029) from the html if present.
         params.body_html = params.body_html.replace(/([\u2028]|[\u2029])/g, '\n');
     };
   });
 
+  // Run a given function. If it is already running, wait for it to finish before running again.
+  // If multiple requests are made before the first request finishes, all but the last will be ignored.
+  // This prevents overwhelming the server with redundant queries during e.g. an autocomplete search while the user types.
+  // Given function should return an angular promise. crmThrottle will deliver the contents when resolved.
+  angular.module('crmUtil').factory('crmThrottle', function($q) {
+    var pending = [],
+      executing = [];
+    return function(func) {
+      var deferred = $q.defer();
+
+      function checkResult(result, success) {
+        _.pull(executing, func);
+        if (_.includes(pending, func)) {
+          runNext();
+        } else if (success) {
+          deferred.resolve(result);
+        } else {
+          deferred.reject(result);
+        }
+      }
+
+      function runNext() {
+        executing.push(func);
+        _.pull(pending, func);
+        func().then(function(result) {
+          checkResult(result, true);
+        }, function(result) {
+          checkResult(result, false);
+        });
+      }
+
+      if (!_.includes(executing, func)) {
+        runNext();
+      } else if (!_.includes(pending, func)) {
+        pending.push(func);
+      }
+      return deferred.promise;
+    };
+  });
+
 })(angular, CRM.$, CRM._);