Определение того, отменен ли AJAX или получена ошибка в перехватчике AngularJs
У меня есть "аннулируемый" angularJs $http
вызов следующим образом:
var defer = $q.defer()
$http.post("http://example.com/someUrl", {some: "data"}, {timeout: defer.promise})
И я отменяю этот запрос ajax, используя defer.resolve()
, потому что это требует некоторая логика.
Где-то где-то в моем коде, у меня есть итератор вроде этого:
angular.module("services.interceptor", arguments).config(function($httpProvider) {
$httpProvider.interceptors.push(function($q) {
return {
responseError: function(rejection) {
if(rejection.status == 0) {
alert("check your connection");
}
return $q.reject(rejection);
}
};
});
});
});
Проблема:
Если есть проблема подключения к Интернету , ajax не работает со статусом 0, а перехватчик поймает его.
Если ajax отменяется с помощью ожидания времени ожидания, чем статус также 0, а перехватчик поймает его.
Я не могу узнать, отменено или получено сообщение об ошибке в обработчике responseError
.
Мой наивный подход заключается в том, чтобы проверить, задан ли тайм-аут следующим образом:
responseError: function(rejection) {
if(rejection.status == 0 && !rejection.config.timeout) {
alert("check your connection");
}
return $q.reject(rejection);
}
Это только гарантирует, что есть условие таймаута для этого запроса, а не , из-за которого оно не получилось. Но это лучше, чем ничего.
Есть ли действительно эффективный способ определения того, был ли ajax поврежден или отменен?
Я использую AngularJs 1.1.5
Ответы
Ответ 1
Я закончил это с помощью флага, который проверяется в обработчике ошибок.
Я начал следовать рекомендациям в конце этой проблемы AngularJS Github: https://github.com/angular/angular.js/issues/1159#issuecomment-25735438
Это привело меня к созданию службы, которая использовала обещание вручную "тайм-аут" вызова http - как вы это делали ниже - путем копирования связанного плунга из этой проблемы: http://plnkr.co/edit/P8wKns51GVqw5mwS5l5R?p=preview
Но это было не совсем полным, так как это не затрагивало проблему, которую вы подняли; в обработке ошибок, как определить, был ли вызов подлинным отключением/тайм-аутом сервера, или просто тайм-аут с ручным запуском. В конце концов, мне пришлось прибегнуть к его сохранению в другой переменной (подобно обещанию тайм-аута), который можно увидеть в этом плунге: http://plnkr.co/edit/BW6Zwu
Основной метод этого кода выглядит следующим образом
return function (url) {
var cancelQuery = null;
var queryCancelled = false;
return function runQuery(query) {
if (cancelQuery) {
queryCancelled = true;
cancelQuery.resolve();
}
cancelQuery = $q.defer();
return $http.
get(url, { params: { query: query }, timeout: cancelQuery.promise }).
then(function (response) {
cancelQuery = null;
return response.data;
}, function (error) {
if(queryCancelled) {
console.log("Request cancelled");
queryCancelled = false;
} else {
console.log("Actual Error !");
}
});
};
};
Не слишком элегантный, но, похоже, он работает и избегает любых неприятных условий гонки из того, что я наблюдал.
Ответ 2
Если вы отмените запрос с помощью timeout
, таймаут будет указан в конфигурации ответа.
Проверьте .config.timeout
в объекте ответа:
function config($httpProvider) {
$httpProvider.interceptors.push(function ($q, $injector) {
return {
request: function (config) {
return config;
},
response: function (response) {
return response || $q.when(response);
},
responseError: function (response) {
switch (response.status) {
case -1:
case 500 :
case 501 :
case 502 :
case 503 :
case 504 :
// the webapp sometimes aborts network requests. Those that dropped should not be restarted
if (response.status === -1 && response.config.timeout) {
break;
}
return retryRequest(response.config, response.status);
}
// otherwise
return $q.reject(response);
}
};
});
}
Ответ 3
Я знаю, что вопрос уже немного старше, но я натолкнулся на его поиск решения для той же проблемы. Кроме того, что в ответе уже дано, я не хочу проверять флаг, который я установил одновременно с тем, что я искал запрос AJAX. Это может быть уже новый запрос, который не был отменен.
Итак, я нашел для меня другое решение. Возможно, кто-то может его использовать, тем не менее, это не актуальный вопрос.
Квинтэссенцией моего решения является проверка не только статуса. Если данные также являются нулевыми, это был запрос cancled.
var defer = $q.defer()
$http.get("http://example.com/someUrl", {timeout: defer.promise})
.error(function(data, status){
if (data === null && status === 0) {
// the request was cancled
}
});
Ответ 4
Вы можете использовать (rejection.config.timeout.status!= 499):
...timeout.promise.status = 499;
...timeout.resolve();
responseError: function(rejection) {
if(rejection.status == 0 && rejection.config.timeout.status != 499) {
alert("check your connection");
}
return $q.reject(rejection);
}
вместо:
responseError: function(rejection) {
if(rejection.status == 0 && !rejection.config.timeout) {
alert("check your connection");
}
return $q.reject(rejection);
}
Кстати, в этом случае вам не нужен дополнительный декоратор запросов.
Ответ 5
Мне удалось определить, был ли запрос отменен в Chrome v55, проверив тип свойства глобального объекта события на строку "abort" (код ниже). У меня возникла проблема, что текущие запросы были отменены при переходе на другую страницу и вызвали ответError.
angular.module("services.interceptor", arguments)
.config(function($httpProvider) {
$httpProvider.interceptors.push(function($q, $window) {
return {
responseError: function(rejection) {
if($window.event.type === "abort") {
// handle cancelled here
} else if(rejection.status < 1) {
alert("check your connection");
}
return $q.reject(rejection);
}
};
});
});
});
(также: свойство состояния, похоже, изменилось с 0 на -1 на сетевые ошибки, поэтому я написал rejection.status < 1
))