فهرست منبع

fix race condition with removed event source still rendering events

Adam Shaw 9 سال پیش
والد
کامیت
9c5d6db144
2فایلهای تغییر یافته به همراه81 افزوده شده و 10 حذف شده
  1. 41 10
      src/EventManager.js
  2. 40 0
      tests/automated/removeEventSource.js

+ 41 - 10
src/EventManager.js

@@ -112,9 +112,13 @@ function EventManager(options) { // assumed to be a calendar
 			var i, eventInput;
 			var abstractEvent;
 
-			// is this the source's most recent fetch?
-			if (fetchId === source._fetchId) {
-
+			if (
+				// is this the source's most recent fetch?
+				// if not, rely on an upcoming fetch of this source to decrement pendingSourceCnt
+				fetchId === source._fetchId &&
+				// event source no longer valid?
+				source._status !== 'rejected'
+			) {
 				source._status = 'resolved';
 
 				if (eventInputs) {
@@ -137,13 +141,29 @@ function EventManager(options) { // assumed to be a calendar
 					}
 				}
 
-				pendingSourceCnt--;
-				if (!pendingSourceCnt) {
-					reportEvents(cache);
-				}
+				decrementPendingSourceCnt();
 			}
 		});
 	}
+
+
+	function rejectEventSource(source) {
+		var wasPending = source._status === 'pending';
+
+		source._status = 'rejected';
+
+		if (wasPending) {
+			decrementPendingSourceCnt();
+		}
+	}
+
+
+	function decrementPendingSourceCnt() {
+		pendingSourceCnt--;
+		if (!pendingSourceCnt) {
+			reportEvents(cache);
+		}
+	}
 	
 	
 	function _fetchEventSource(source, callback) {
@@ -320,14 +340,25 @@ function EventManager(options) { // assumed to be a calendar
 	}
 
 
-	function removeEventSource(source) {
+	function removeEventSource(targetSource) {
+
+		// cancel pending requests
+		for (var i = 0; i < sources.length; i++) {
+			if (isSourcesEqual(sources[i], targetSource)) {
+				rejectEventSource(sources[i]);
+			}
+		}
+
+		// remove from source list
 		sources = $.grep(sources, function(src) {
-			return !isSourcesEqual(src, source);
+			return !isSourcesEqual(src, targetSource);
 		});
+
 		// remove all client events from that source
 		cache = $.grep(cache, function(e) {
-			return !isSourcesEqual(e.source, source);
+			return !isSourcesEqual(e.source, targetSource);
 		});
+
 		reportEvents(cache);
 	}
 

+ 40 - 0
tests/automated/removeEventSource.js

@@ -52,6 +52,46 @@ describe('removeEventSource', function() {
 		});
 	});
 
+	it('won\'t render removed events when subsequent addEventSource', function(done) {
+
+		var source1 = function(start, end, timezone, callback) {
+			setTimeout(function() {
+				callback([{
+					title: 'event1',
+					className: 'event1',
+					start: '2014-08-01T02:00:00'
+				}]);
+			}, 100);
+		};
+
+		var source2 = function(start, end, timezone, callback) {
+			setTimeout(function() {
+				callback([{
+					title: 'event2',
+					className: 'event2',
+					start: '2014-08-01T02:00:00'
+				}]);
+			}, 100);
+		};
+
+		options.eventSources = [ source1 ];
+
+		options.eventAfterAllRender = function() {
+			if (!$('.fc-event').length) {
+				; // might have rendered no events after removeEventSource call
+			}
+			else {
+				expect($('.event1').length).toBe(0);
+				expect($('.event2').length).toBe(1);
+				done();
+			}
+		};
+
+		$('#cal').fullCalendar(options);
+		$('#cal').fullCalendar('removeEventSource', source1);
+		$('#cal').fullCalendar('addEventSource', source2);
+	});
+
 	function testInput(eventInput) {
 
 		it('correctly removes events provided via `events` at initialization', function(done) {