I'm trying to improve my app performance on mobile devices with laggy networks.
The first step was add "global" timeout for all http request, I used simple request interceptor for that - request(config) { return angular.extend({ timeout: 30000 }, config) }. It worked fine - instead of infinite waiting for the response I could display a warning about laggy network (in the responseError interceptor), moreover the slow request is canceled so I can expect that it should free some bandwidth for the other requests.
Now I'm trying to implement another optimization - cancelling pending http requests before the uiRouter state change because there's no reason for loading resources for the state which is no longer going to be displayed. For instance a case when a user is trying to navigate to state A, A resolvables are pending, bored user changed his mind and he's trying to navigate to state B.
My current implementation is base on $q and $timeout services and custom service for collecting timeouts for all http requests and cancelling them in a batch when necessary. Enough words, here's the code:
const REQUEST_TIMEOUT = 3000; // 30 secs
function httpRequestsCancellerService($timeout, $transitions, $q) {
const cancelers = [];
function cancelAll() {
while (cancelers.length > 0) {
const canceler = cancelers.pop();
canceler.resolve();
}
}
// Cancel all pending http requests before the next transition
$transitions.onStart({}, cancelAll);
return {
createTimeout() {
const canceler = $q.defer();
// TODO it will keep running even whe the request is completed or failed
$timeout(canceler.resolve, REQUEST_TIMEOUT);
cancelers.push(canceler);
return canceler.promise;
}
};
}
function timeoutInterceptor(httpRequestsCanceller) {
return {
request(config) {
const timeout = httpRequestsCanceller.createTimeout();
return angular.extend({ timeout }, config);
}
};
}
module.exports = function($httpProvider, $provide) {
'ngInject';
$provide.service('httpRequestsCanceller', httpRequestsCancellerService);
$httpProvider.interceptors.push(timeoutInterceptor);
};
It works perfectly now but it has a small drawback - $timeout in the request interceptor will keep running and it will finally resolve the canceller even if the request is completed or failed.
The question is - should I care about these pending $timeouts? Is it necessary to $timeout.cancel them in order to free some resources or avoid some strange side effects?
Related
I'm working on a web application using vue2 and axios. After a certain event, I want to cancel every request that I've made before. For this, I'm using an AbortController that I declared in my store:
Example of a request :
await axios.post('...', this.dataToSend, {signal: this.controller.signal});
In my store, I have a function abort :
const state = {
controller: new AbortController()
}
const actions = {
abort ({state}) {
state.controller.abort();
}
}
I have multiple requests with the same controller signal. Is there a way to see directly in the abort method all the request that has been aborted durectly from my controller object ?
.service('searchService', function ($http, $state) {
getSearchResult: function (query) {
return $http.get('/api/search/' + query + '/simple_search.json');
}
});
What I need to do is destroy all the pending search request so that when a new keyword is entered only 1 request stays in process. Right now when my server is getting slow multiple http request stays in pending state so I just want to cancel the previous request each time a new request is initiated.
*This is required for handling multiple requests.
In my Meteor 1.0 app, I'm running a batch of server-side HTTP requests in order to retrieve fixture data in a synchronous fashion. Once a request completes and computations are run on that data, startNumber is incremented (by 5000) and the request is re-run with that new value. This loop system will continue until the API returns a blank response array, signifying all the data has been captured. This HTTP request is part of a larger, complex function that helps set the context of the request.
functionName = function(param1,param2,param3) {
// ...
// ...
var startNumber = 1;
do {
var request = Meteor.http.call("GET", "https://url-to-api-endpoint",
{ params:
{
"since": startNumber
},
timeout: 60000
}
);
if(request.statusCode === 200) {
var response = request.data;
// perform calculations on the response
startNumber+=5000;
}
} (while response.length>0);
// ...
// ...
};
The do-while loop system is working fine, except that every few iterations the request is returning with Error: getaddrinfo ENOTFOUND. The URL is perfectly valid, and it appears these errors are resulting from a finicky/unreliable API as sometimes the same exact request will go through or error out. I want to replay failed requests in order to make sure my app is retrieving data chronologically before proceeding.
How can I replay a failed HTTP request as though it were being run for the first time? In other words, without losing the current context of all the variables, etc., in functionName?
FYI, incase someone else ends up in this predicament, I solved this problem by wrapping the HTTP request in a try-catch block. In the case of an error such as getaddrinfo ENOTFOUND or ETIMEDOUT, the error gets caught. Within the catch block, I call the functionName and pass in parameters for the current state (i.e. the current startNumber) - this allows me to essentially "replay" the request all over again.
// ...
// ...
try {
var request = Meteor.http.call("GET", "https://url-to-api-endpoint",
{ params:
{
"since": startNumber
},
timeout: 60000
}
);
} catch(err) {
console.log(err + '\nFailed...retrying');
functionName(param1,param2,param3);
}
// ...
// ...
Is there a way to cancel requests/queries to Elasticsearch using elasticjs? The web app I am working on performs a request/query every 5 seconds, but I would like to cancel the request if for some reason the response doesn't show up in 5 seconds (so the browser doesn't pile up a bunch of requests that are unnecessary since the queries are happening repeatedly). I understand this would not prevent Elasticsearch from completing the query, but I would like to at least cancel the request in the browser.
Example:
var request = ejs.Request().doSearch();
var dataFromElasticsearch;
request.then(function (data) {
dataFromElasticsearch = data;
});
setTimeout(function () {
if (!dataFromElasticsearch) {
//do something here to cancel request
}
}, 5000)
Per documentation for elasticsearch.js (3.1, at the time of writing):
...calling the API will return an object (either a promise or just a plain object) which has an abort() method. Calling that abort method ends the HTTP request, but it will not end the work Elasticsearch is doing.
Specifically for your example:
setTimeout(function () {
if (!dataFromElasticsearch) {
request.abort();
}
}, 5000)
I have a use case where my http requests are caching the intermediate result on server.
If the cache is not present the request builds it by requesting another server.
These requests are fired in succession (loop) using AJAX to Node Server and the number of requests can be in range of 50 to 500.
The Problem:
Since the requests are made in a loop and the cache is already not present first few of them all try to build the cache and sometimes consequent requests find the semi-built cache, which returns wrong result.
I can circumvent this problem with polling:
(function next(){
if(!wait){
fs.readFile(cacheFile, function(err){
if(err) {
wait = true;
createCache(); // sets wait = false;
} else {
useCache();
}
});
} else {
setTimeout(next,waitTime);
}
})();
My Query:
Can the requests be halted without polling, and continue only after the first request has completed the cache building process?
Yes, it is possible in combination with Futures/Promise. You can take this one.
Outside of the scope define var cachePromise and you can use something like this below:
if (!cachePromise) {
cachePromise = require('future').create()
buildCache(function() {
cachePromise.fulfill();
});
}
cachePromise.when(next); // this one triggers next route in middleware stack
Put the code in route stack before the route which gives result and you are good to go.
thanks.