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:
const Person = DefineMap.extend( {
first: "string",
last: "string"
} );
const 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:
import canBatch from "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:
const Todo = DefineMap.extend( {
name: "string",
complete: "boolean"
} );
const 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!
}
});