From: Tim Otten Date: Thu, 16 Apr 2015 03:47:24 +0000 (-0700) Subject: crmUtil - Add crmQueue() helper for serializing crmApi() calls X-Git-Url: https://vcs.fsf.org/?a=commitdiff_plain;h=a4f8f9005e483423c02ebdcbd576086a44ec7998;p=civicrm-core.git crmUtil - Add crmQueue() helper for serializing crmApi() calls --- diff --git a/ang/crmUtil.js b/ang/crmUtil.js index f64292762b..b6a0d01310 100644 --- a/ang/crmUtil.js +++ b/ang/crmUtil.js @@ -193,6 +193,40 @@ }; }]); + // Wrap an async function in a queue, ensuring that independent async calls are issued in strict sequence. + // usage: qApi = crmQueue(crmApi); qApi(entity,action,...).then(...); qApi(entity2,action2,...).then(...); + // This is similar to promise-chaining, but allows chaining independent procs (without explicitly sharing promises). + angular.module('crmUtil').factory('crmQueue', function($q) { + // @param worker A function which generates promises + return function crmQueue(worker) { + var queue = []; + function next() { + var task = queue[0]; + worker.apply(null, task.a).then( + function onOk(data) { + queue.shift(); + task.dfr.resolve(data); + if (queue.length > 0) next(); + }, + function onErr(err) { + queue.shift(); + task.dfr.reject(err); + if (queue.length > 0) next(); + } + ); + } + function enqueue() { + var dfr = $q.defer(); + queue.push({a: arguments, dfr: dfr}); + if (queue.length === 1) { + next(); + } + return dfr.promise; + } + return enqueue; + }; + }); + // Adapter for CRM.status which supports Angular promises (instead of jQuery promises) // example: crmStatus('Saving', crmApi(...)).then(function(result){...}) angular.module('crmUtil').factory('crmStatus', function($q){ diff --git a/tests/karma/unit/crmUtilSpec.js b/tests/karma/unit/crmUtilSpec.js index 8c0daedb4f..3e32dbf06d 100644 --- a/tests/karma/unit/crmUtilSpec.js +++ b/tests/karma/unit/crmUtilSpec.js @@ -103,4 +103,46 @@ describe('crmUtil', function() { }); }); + + describe('crmQueue', function() { + var crmQueue, $q, $rootScope, $timeout; + + beforeEach(inject(function(_crmQueue_, _$rootScope_, _$q_, _$timeout_) { + crmQueue = _crmQueue_; + $rootScope = _$rootScope_; + $q = _$q_; + $timeout = _$timeout_; + })); + + function addAfterTimeout(a, b, ms) { + var dfr = $q.defer(); + $timeout(function(){ + dfr.resolve(a+b); + }, ms); + return dfr.promise; + } + + it('returns in order', function(done) { + var last = null; + var queuedFunc = crmQueue(addAfterTimeout); + // note: the queueing order is more important the timeout-durations (15ms vs 5ms) + queuedFunc(1, 2, 25).then(function(sum) { + expect(last).toBe(null); + expect(sum).toBe(3); + last = sum; + }); + queuedFunc(3, 4, 5).then(function(sum){ + expect(last).toBe(3); + expect(sum).toBe(7); + last = sum; + done(); + }); + + for (var i = 0; i < 5; i++) { + $rootScope.$apply(); + $timeout.flush(20); + } + }); + }); + });