CiviCRM Community Forums (archive)

*

News:

Have a question about CiviCRM?
Get it answered quickly at the new
CiviCRM Stack Exchange Q+A site

This forum was archived on 25 November 2017. Learn more.
How to get involved.
What to do if you think you've found a bug.



  • CiviCRM Community Forums (archive) »
  • Old sections (read-only, deprecated) »
  • Developer Discussion (Moderator: Donald Lobo) »
  • crmQueue(crmApi)
Pages: [1]

Author Topic: crmQueue(crmApi)  (Read 298 times)

totten

  • Administrator
  • Ask me questions
  • *****
  • Posts: 695
  • Karma: 64
crmQueue(crmApi)
April 16, 2015, 07:49:27 pm
Thought this was a curious bit of code.

https://github.com/civicrm/civicrm-core/pull/5644/files#diff-d08e5d3ceca1972df6d467b2824ffab5R101

Suppose you have some Civi/AngularJS code like this:

Code: [Select]
crmApi('Foo', 'create', ...).then( ... a ... );
crmApi('Bar', 'create', ...).then( ... b ... );

This could fire off two concurrent AJAX requests. That's usually good for performance, but if the two calls update related tables, then it increases the odds of contention. So we want to run the calls in sequential order. With promises, one could do that as:

Code: [Select]
crmApi('Foo', 'create', ...)
.then( ... a ... )
.then(function(){
  return crmApi('Bar','create', ...);
})
.then( ... b ... );

This is a pretty nice pattern. The CiviMail/Angular code uses it a lot. But it still has an issue -- each step in the chain returns a different promise. If 'Foo.create' and 'Bar.create' are initiated by independent components, then it becomes tricky to use the correct promise for each.

So #5644 takes the approach of putting a queue in front of crmApi():

Code: [Select]
var qApi = crmQueue(crmApi);
qApi(‘Foo’, ‘create’, ...).then( ... a ... );
qApi(‘Bar’, ‘create’, ...).then( ... b ... );

There are a couple neat things about this: the qApi() calls can be in separate places; but as long as they all get a handle to qApi, they'll stay in order. Also, it doesn't force any particular lifecycle/visibility for the queue. For example, any of these work:

Code: [Select]
// Global/Singleton
window.singletonQueue = crmQueue(crmApi);
window.singletonQueue('Entity', 'action', ...);

// Named globals
window.namedQueues['foo'] = crmQueue(crmApi);
window.namedQueues['foo']('Entity', 'action', ...);

// Private queue shared by callbacks within a controller
angular.module('foo').controller('FooCtrl', function(crmQueue, crmApi){
  var privateQueue = crmQueue(crmApi);
  privateQueue('Entity', 'action', ...);
});

// Injectable service; shared among different controllers/directives
angular.module('foo').service('crmQueuedApi', function(crmQueue, crmApi){
  return crmQueue(crmApi);
});
angular.module('foo').controller('FooCtrl', function(crmQueuedApi){
  crmQueuedApi('Entity', 'action', ...);
});

If I ever get around to implementing https://gist.github.com/totten/23858dc6001208228d49 , then we should be able to combine queuing and batching -- e.g. the queue has two tasks to execute sequentially, and each task is a separate batch of concurrent steps:

Code: [Select]
var qApi = crmQueue(crmApi);
var batch1 = crmBatcher(qApi);
batch1('Entity1', 'action1').then(...);
batch1('Entity2', 'action2').then(...);
batch1('Entity3', 'action3').then(...);
batch1.run();
var batch2 = crmBatcher(qApi);
batch2('Entity4', 'action4').then(...);
batch2('Entity5', 'action5').then(...);
batch2('Entity6', 'action6').then(...);
batch2.run();

Pages: [1]
  • CiviCRM Community Forums (archive) »
  • Old sections (read-only, deprecated) »
  • Developer Discussion (Moderator: Donald Lobo) »
  • crmQueue(crmApi)

This forum was archived on 2017-11-26.