aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorIgor Minar2010-12-04 23:49:26 -0800
committerIgor Minar2010-12-06 16:45:59 -0800
commit011fa39c2a0b5da843395b538fc4e52e5ade8287 (patch)
treeb5cc7ee72fb2fbcc76da2588822a21c2cedb614c /src
parent58d0e8945d772eddbfecbe6a645b2f1c4dd38bf2 (diff)
downloadangular.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.js3
-rw-r--r--src/Browser.js59
-rw-r--r--src/services.js42
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']);
/**