diff options
| author | Igor Minar | 2010-12-04 23:49:26 -0800 |
|---|---|---|
| committer | Igor Minar | 2010-12-06 16:45:59 -0800 |
| commit | 011fa39c2a0b5da843395b538fc4e52e5ade8287 (patch) | |
| tree | b5cc7ee72fb2fbcc76da2588822a21c2cedb614c /src | |
| parent | 58d0e8945d772eddbfecbe6a645b2f1c4dd38bf2 (diff) | |
| download | angular.js-011fa39c2a0b5da843395b538fc4e52e5ade8287.tar.bz2 | |
add $browser.defer and $defer service and fix async xhr cache issue
- Closes #152 ($resource().query() sometimes calls callback before
returning, and it shouldn't)
- add $browser.defer method
- add $defer service
- integrate $browser.defer with outstandingRequests counter in $browser
- fix all old tests that relied on buggy behavior
Diffstat (limited to 'src')
| -rw-r--r-- | src/AngularPublic.js | 3 | ||||
| -rw-r--r-- | src/Browser.js | 59 | ||||
| -rw-r--r-- | src/services.js | 42 |
3 files changed, 83 insertions, 21 deletions
diff --git a/src/AngularPublic.js b/src/AngularPublic.js index 98e0eed8..af572340 100644 --- a/src/AngularPublic.js +++ b/src/AngularPublic.js @@ -15,7 +15,8 @@ angularService('$browser', function($log){ jqLite(window.document), jqLite(window.document.getElementsByTagName('head')[0]), XHR, - $log); + $log, + window.setTimeout); browserSingleton.startPoller(50, function(delay, fn){setTimeout(delay,fn);}); browserSingleton.bind(); } diff --git a/src/Browser.js b/src/Browser.js index 197cf1f4..94807a8c 100644 --- a/src/Browser.js +++ b/src/Browser.js @@ -8,7 +8,7 @@ var XHR = window.XMLHttpRequest || function () { throw new Error("This browser does not support XMLHttpRequest."); }; -function Browser(location, document, head, XHR, $log) { +function Browser(location, document, head, XHR, $log, setTimeout) { var self = this; self.isMock = false; @@ -19,6 +19,28 @@ function Browser(location, document, head, XHR, $log) { var outstandingRequestCount = 0; var outstandingRequestCallbacks = []; + + /** + * Executes the `fn` function (supports currying) and decrements the `outstandingRequestCallbacks` + * counter. If the counter reaches 0, all the `outstandingRequestCallbacks` are executed. + */ + function completeOutstandingRequest(fn) { + try { + fn.apply(null, slice.call(arguments, 1)); + } finally { + outstandingRequestCount--; + if (outstandingRequestCount === 0) { + while(outstandingRequestCallbacks.length) { + try { + outstandingRequestCallbacks.pop()(); + } catch (e) { + $log.error(e); + } + } + } + } + } + /** * @workInProgress * @ngdoc method @@ -58,19 +80,7 @@ function Browser(location, document, head, XHR, $log) { outstandingRequestCount ++; xhr.onreadystatechange = function() { if (xhr.readyState == 4) { - try { - callback(xhr.status || 200, xhr.responseText); - } finally { - outstandingRequestCount--; - if (outstandingRequestCount === 0) { - while(outstandingRequestCallbacks.length) { - try { - outstandingRequestCallbacks.pop()(); - } catch (e) { - } - } - } - } + completeOutstandingRequest(callback, xhr.status || 200, xhr.responseText); } }; xhr.send(post || ''); @@ -250,6 +260,27 @@ function Browser(location, document, head, XHR, $log) { } }; + + /** + * @workInProgress + * @ngdoc + * @name angular.service.$browser#defer + * @methodOf angular.service.$browser + * + * @description + * Executes a fn asynchroniously via `setTimeout(fn, 0)`. + * + * Unlike when calling `setTimeout` directly, in test this function is mocked and instead of using + * `setTimeout` in tests, the fns are queued in an array, which can be programaticaly flushed via + * `$browser.defer.flush()`. + * + * @param {function()} fn A function, who's execution should be defered. + */ + self.defer = function(fn) { + outstandingRequestCount++; + setTimeout(function() { completeOutstandingRequest(fn); }, 0); + }; + ////////////////////////////////////////////////////////////// // Misc API ////////////////////////////////////////////////////////////// diff --git a/src/services.js b/src/services.js index 91932b44..ef3de549 100644 --- a/src/services.js +++ b/src/services.js @@ -685,7 +685,7 @@ angularServiceInject('$route', function(location) { * @ngdoc service * @name angular.service.$xhr * @requires $browser - * @requires $error + * @requires $xhr.error * @requires $log * * @description @@ -801,6 +801,36 @@ angularServiceInject('$xhr.bulk', function($xhr, $error, $log){ return bulkXHR; }, ['$xhr', '$xhr.error', '$log']); + +/** + * @workInProgress + * @ngdoc service + * @name angular.service.$defer + * @requires $browser + * @requires $log + * + * @description + * Delegates to {@link angular.service.$browser.defer $browser.defer}, but wraps the `fn` function + * into a try/catch block and delegates any exceptions to + * {@link angular.services.$exceptionHandler $exceptionHandler} service. + * + * In tests you can use `$browser.defer.flush()` to flush the queue of deferred functions. + * + * @param {function()} fn A function, who's execution should be deferred. + */ +angularServiceInject('$defer', function($browser, $exceptionHandler) { + return function(fn) { + $browser.defer(function() { + try { + fn(); + } catch(e) { + $exceptionHandler(e); + } + }); + }; +}, ['$browser', '$exceptionHandler']); + + /** * @workInProgress * @ngdoc service @@ -811,7 +841,7 @@ angularServiceInject('$xhr.bulk', function($xhr, $error, $log){ * * @example */ -angularServiceInject('$xhr.cache', function($xhr){ +angularServiceInject('$xhr.cache', function($xhr, $defer){ var inflight = {}, self = this; function cache(method, url, post, callback, verifyCache){ if (isFunction(post)) { @@ -819,9 +849,9 @@ angularServiceInject('$xhr.cache', function($xhr){ post = _null; } if (method == 'GET') { - var data; - if (data = cache.data[url]) { - callback(200, copy(data.value)); + var data, dataCached; + if (dataCached = cache.data[url]) { + $defer(function() { callback(200, copy(dataCached.value)); }); if (!verifyCache) return; } @@ -853,7 +883,7 @@ angularServiceInject('$xhr.cache', function($xhr){ cache.data = {}; cache.delegate = $xhr; return cache; -}, ['$xhr.bulk']); +}, ['$xhr.bulk', '$defer']); /** |
