can-event/batch/batch
Adds task batching abilities to event dispatching.
Object
The can-event/batch/batch
module adds task batching abilities to
the can-event module. It:
- Provides a queue method to add batched work.
- Provides dispatch and overwrites can-event.dispatch to use the task queue when dispatching events.
- Provides a start and stop method that can create a new task queue.
- Provides collecting which returns the queue collecting tasks.
- Provides dispatching which returns the queue dispatching tasks.
- Dispatches
batchEnd
when a queue's tasks have been completed.
Use
To batch events, call start, then make changes that dispatch batched events, then call stop.
For example, a map might have a first
and last
property:
var Person = DefineMap.extend({
first: "string",
last: "string"
});
var baby = new Person({first: "Roland", last: "Shah"});
Normally, when baby
's first
and last
are fired, those events are dispatched immediately:
baby.on("first", function(ev, newFirst){
console.log("first is "+newFirst);
}).on("last", function(ev, newLast){
console.log("last is "+newLast);
});
baby.first = "Ramiya";
// console.logs -> "first is Ramiya"
baby.last = "Meyer";
// console.logs -> "first is Meyer"
However, if a batch is used, events will not be dispatched until stop is called:
var canBatch = require("can-event/batch/batch");
canBatch.start();
baby.first = "Lincoln";
baby.last = "Sullivan";
canBatch.stop();
// console.logs -> "first is Lincoln"
// console.logs -> "first is Sullivan"
Performance
CanJS synchronously dispatches events when a property changes. This makes certain patterns easier. For example, if you are utilizing live-binding and change a property, the DOM is immediately updated.
Occasionally, you may find yourself changing many properties at once. To
prevent live-binding from performing unnecessary updates,
update the properties within a pair of calls to canBatch.start
and
canBatch.stop
.
Consider a todo list with a completeAll
method that marks every todo in the list as
complete and completeCount
that counts the number of complete todos:
var Todo = DefineMap.extend({
name: "string",
complete: "boolean"
});
var TodoList = DefineList.extend({
"#": Todo,
completeAll: function(){
this.forEach(function(todo){
todo.complete = true;
})
},
completeCount: function(){
return this.filter({complete: true}).length;
}
})
And a template that uses the completeCount
and calls completeAll
:
<ul>
{{#each todos}}
<li><input type='checklist' checked:bind="complete"/> {{name}}</li>
{{/each}}
</ul>
<button on:click="todos.completeAll()">
Complete {{todos.completeCount}} todos
</button>
When completeAll
is called, the {{todos.completeCount}}
magic tag will update
once for every completed count. We can prevent this by wrapping completeAll
with calls to
start
and stop
:
completeAll: function(){
canBatch.start();
this.forEach(function(todo){
todo.complete = true;
});
canBatch.end();
},
batchNum
All events created within a set of start
/ stop
calls share the same
batchNum value. This can be used to respond only once for a given batchNum.
var batchNum;
person.on("name", function(ev, newVal, oldVal) {
if(!ev.batchNum || ev.batchNum !== batchNum) {
batchNum = ev.batchNum;
// your code here!
}
});