diff --git src/wp-admin/js/customize-controls.js src/wp-admin/js/customize-controls.js
index 7a0f3e3..4a3d93a 100644
--- src/wp-admin/js/customize-controls.js
+++ src/wp-admin/js/customize-controls.js
@@ -952,36 +952,55 @@
 					query = $.extend( this.query(), {
 						action: 'customize_save',
 						nonce:  this.nonce.save
-					}),
-					request = $.post( api.settings.url.ajax, query );
-
-				api.trigger( 'save', request );
-
-				body.addClass('saving');
+					} ),
+					processing = api.state( 'processing' ),
+					submit_when_done_processing,
+					submit;
+
+				body.addClass( 'saving' );
+
+				submit = function () {
+					var request = $.post( api.settings.url.ajax, query );
+
+					api.trigger( 'save', request );
+
+					request.always( function () {
+						body.removeClass( 'saving' );
+					} );
+
+					request.done( function( response ) {
+						// Check if the user is logged out.
+						if ( '0' === response ) {
+							self.preview.iframe.hide();
+							self.login().done( function() {
+								self.save();
+								self.preview.iframe.show();
+							} );
+							return;
+						}
 
-				request.always( function() {
-					body.removeClass('saving');
-				});
+						// Check for cheaters.
+						if ( '-1' === response ) {
+							self.cheatin();
+							return;
+						}
 
-				request.done( function( response ) {
-					// Check if the user is logged out.
-					if ( '0' === response ) {
-						self.preview.iframe.hide();
-						self.login().done( function() {
-							self.save();
-							self.preview.iframe.show();
-						});
-						return;
-					}
+						api.trigger( 'saved' );
+					} );
+				};
 
-					// Check for cheaters.
-					if ( '-1' === response ) {
-						self.cheatin();
-						return;
-					}
+				if ( 0 === processing() ) {
+					submit();
+				} else {
+					submit_when_done_processing = function () {
+						if ( 0 === processing() ) {
+							api.state.unbind( 'change', submit_when_done_processing );
+							submit();
+						}
+					};
+					api.state.bind( 'change', submit_when_done_processing );
+				}
 
-					api.trigger( 'saved' );
-				});
 			}
 		});
 
@@ -1016,8 +1035,9 @@
 		// Save and activated states
 		(function() {
 			var state = new api.Values(),
-				saved = state.create('saved'),
-				activated = state.create('activated');
+				saved = state.create( 'saved' ),
+				activated = state.create( 'activated' ),
+				processing = state.create( 'processing' );
 
 			state.bind( 'change', function() {
 				var save = $('#save'),
@@ -1040,6 +1060,7 @@
 			// Set default states.
 			saved( true );
 			activated( api.settings.theme.active );
+			processing( 0 );
 
 			api.bind( 'change', function() {
 				state('saved').set( false );
diff --git src/wp-admin/js/customize-widgets.js src/wp-admin/js/customize-widgets.js
index fc6028b..9ea91b3 100644
--- src/wp-admin/js/customize-widgets.js
+++ src/wp-admin/js/customize-widgets.js
@@ -914,7 +914,7 @@ var WidgetCustomizer = ( function ($) {
 			var control = this,
 				widget_content,
 				save_btn,
-				trigger_save;
+				update_widget_debounced;
 
 			widget_content = control.container.find( '.widget-content' );
 
@@ -928,7 +928,7 @@ var WidgetCustomizer = ( function ($) {
 				control.updateWidget();
 			} );
 
-			trigger_save = _.debounce( function () {
+			update_widget_debounced = _.debounce( function () {
 				// @todo For compatibility with other plugins, should we trigger a click event? What about form submit event?
 				control.updateWidget();
 			}, 250 );
@@ -943,8 +943,10 @@ var WidgetCustomizer = ( function ($) {
 
 			// Handle widgets that support live previews
 			widget_content.on( 'change input propertychange', ':input', function ( e ) {
-				if ( e.type === 'change' || ( this.checkValidity && this.checkValidity() ) ) {
-					trigger_save();
+				if ( e.type === 'change' ) {
+					control.updateWidget();
+				} else if ( this.checkValidity && this.checkValidity() ) {
+					update_widget_debounced();
 				}
 			} );
 
@@ -1097,9 +1099,10 @@ var WidgetCustomizer = ( function ($) {
 				element_id_to_refocus = null,
 				active_input_selection_start = null,
 				active_input_selection_end = null,
-				params = {},
+				params,
 				data,
 				inputs,
+				processing,
 				jqxhr;
 
 			args = $.extend( {
@@ -1129,6 +1132,8 @@ var WidgetCustomizer = ( function ($) {
 
 			control.container.addClass( 'widget-form-loading' );
 			control.container.addClass( 'previewer-loading' );
+			processing = wp.customize.state( 'processing' );
+			processing( processing() + 1 );
 
 			params = {};
 			params.action = self.update_widget_ajax_action;
@@ -1252,6 +1257,8 @@ var WidgetCustomizer = ( function ($) {
 				inputs.each( function () {
 					$( this ).removeData( 'state' + update_number );
 				} );
+
+				processing( processing() - 1 );
 			} );
 		},
 
