UNPKG

can

Version:

MIT-licensed, client-side, JavaScript framework that makes building rich web applications easy.

159 lines (120 loc) 4.49 kB
@page can.batch @parent can.util [can.batch.start `can.batch.start( batchStopHandler )`] and [can.batch.stop `can.batch.stop( force, callStart )`] are used to specify atomic operations. `start` prevents change events from being fired until `stop` is called. This `player` has a favorite `tvshow`. We are listening for any changes to her preferences. var player = new can.Map({ tvshow: "The Simpsons" }); player.bind("change", function(ev, attr, how, newVal, oldVal) { console.log("[change triggered] " + how + ": " + attr); }); Normally, if a favorite `tvshow` were replaced with a favorite `song`, the "change" callback handler would immediately be called when `tvshow` is removed and when `song` is added. By incorporating `can.batch`, the calls to the "change" callback handler will not occur until after `tvshow` is removed, `song` is added, and `can.batch.stop` is called. can.batch.start(); player.removeAttr("tvshow"); player.attr("song", "What makes you beautiful"); can.batch.stop(); Performance and correctness are the two most common reasons to use batch operations. ## Correctness Sometimes, an object can temporarily be in an invalid state. For example, the previous `player` should have a `tvshow` or `song` property, but not both. Event listeners should never be called in an intermediate state. The [can.Map.prototype.define `can/map/define`] plugin uses `can.batch.start` and `can.batch.stop` to accomplish this when calling a `setter`. import "can/map/define/" var Player = can.Map.extend({ define: { tvshow: { set: function(newValue) { this.removeAttr("song"); return newValue; } }, song: { set: function(newValue) { this.removeAttr("tvshow"); return newValue; } } } }); var player = new Player({ song: "Amish Paradise" }); player.bind("change", function(ev, attr, how, newVal, oldVal){ var song = this.attr("song"); var tvshow = this.attr("tvshow"); if(song) { console.log("The greatest song is " + song + ". TV is overrated."); } if(tvshow) { console.log("The greatest TV show is " + tvshow + ". Music is overrated."); } }); player.attr("tvshow", "Breaking Bad"); Use `can.batch.start` and `can.batch.stop` to ensure that events are only triggered when a subject is in a valid state. ## 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 `can.batch.start` and `can.batch.stop`. Consider this list of items. var items = new Items([ {selected: false}, {selected: true}, {selected: false} ]); This template renders the number of selected items. var renderer = can.stache("{{count}}"); $("#itemCount").html(renderer({ count: function() { var count = 0; items.each(function(item) { count += item.attr("selected") ? 1 : 0; }); console.log(count); return count; } })); Using `can.batch` will ensure that the DOM is only updated once the whole list of items has been updated instead of every time an individual item is flipped. $("#selectAll").click(function() { can.batch.start(); items.each(function(item){ item.attr('selected', true); }); can.batch.stop(); }); ## 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; obs.bind("change", function(ev, attr, how, newVal, oldVal) { if(!ev.batchNum || ev.batchNum !== batchNum) { batchNum = ev.batchNum; // your code here! } }); ## Automatic Batching Libraries like Angular and Ember always batch operations. This behavior can be reproduced by batching everything that happens within the same thread of execution and/or within 10ms of each other. can.batch.start(); setTimeout(function() { can.batch.stop(true, true); setTimeout(arguments.callee, 10); }, 10);