Angular $http config timeout - javascript

As documented in the angular docs,
timeout – {number|Promise} – timeout in milliseconds, or promise that should abort the request when resolved.
Right now I am setting timeout to promise, so I can manually cancel the request by promise.resolve().
Right now, I also want to make it capable to config timeout value, instead of having the request timeout being 120 seconds.
How can I configure it without affecting the existing cancel request functionality?

You could do somthing like this
$scope.qPromiseCall = function()
{
var timeoutPromise = $timeout(function()
{
//aborts the request when timed out
canceler.resolve();
console.log("Timed out");
}, 250);
//we set a timeout for 250ms and store the promise in order to be cancelled later if the data does not arrive within 250ms
var canceler = $q.defer();
$http.get("data.js", {timeout: canceler.promise} )
.success(function(data)
{
console.log(data);
$timeout.cancel(timeoutPromise);
//cancel the timer when we get a response within 250ms
});
}
For more details look at
Setting a timeout handler on a promise in angularjs
First Answer by #Khanh TO

Related

Await inside setInterval

I have a function that do some HTTP requests, i need to do them often every certain amount of time (in this case 5 seconds) if a certain condition is triggered.
What happens is that sometimes the requests that are inside the setInterval loop takes more than the specified time and the loop triggers again, calling the request again, without waiting for the previous to resolve.
function doRequests() {
setInterval(async () => {
//Sometimes the following line takes more than 5 seconds to return its promise
const response = await myRequestFunction();
// do something with response...
if(/*certain condition is triggered*/)
{
//Call the function again
doRequests();
}
}, 5000);
}
doRequests();
I've already tried doing a recursive setTimeOut function like in this post, it worked but a few requests later it simply stopped working, not because a stack overflow happened but because IT SIMPLY STOPPED! The request was done, but it never brought the response back so the code stopped. And a few time before it stops it got slow.
I've also already tried using a setTimeOut inside a while loop but it seems that loops doesn't wait for intervals, not within this async context.
So the solution i need is: A way to every 5 seconds do the requests, but if it takes more than that to return the response, await for it.
Well, here is a solution in order to keep the cycle period as most regular as possible. As long the responses arrive within the 5s timeout, the cycle polling should be pretty regular. However, if a response comes in late, the new request is made immediately it is processed.
I also added a try-catch block, because it is likely a chance having some error on a HTTP request (and also because you're doing other stuffs in the while). This leads you to decide how to behave in such a cases.
async function doRequests() {
let answered = false;
let expired = false;
//start the timer
let tmr = setTimeout(function() {
if (answered) {
//response already received (and processed), so start a new request
doRequest();
}
else {
//mark the timer as expired
expired = true;
},
5000
);
try {
const response = await myRequestFunction();
// do something with response...
if(/*certain condition is triggered*/)
{
answered = true;
if (expired) {
//Call the function again
doRequests();
}
}
}
catch (err) {
//error trapped: clear timeout
clearTimeout(tmr);
if (/* decide to continue as it was fine */) {
answered = true;
if (expired) {
//Call the function again
doRequests();
}
}
}
}
doRequests();

How do you wrap a promise chain in $timeout in Angular?

I have a service that gets reports:
ReportsResource.getActiveUsers(). This uses $http and then returns a promise.
I then use it like this:
var request = ReportsResource.getActiveUsers();
request.then(populateActiveUsersTable, handleError);
But, the catch is that the request to get the active users report on the backend can take anywhere from a couple of seconds, to 30+ minutes.
If you make a request and no cached report is available, it generates the report, and then the request waits for data (again, could be 2 seconds or 30 minutes) for that request.
If you make a request and the report is currently being generated, it returns a response instantly telling you the report is not ready yet. At which point you can keep polling to see if the report is ready.
If the report is ready (cached), then it returns the response instantly with the report data.
What I need is wrap the request in a timeout that waits up to 10 seconds, and then aborts if the response takes longer than 10 seconds to complete, and starts polling the server to ask if the report is ready yet. But if the request resolves under 10 seconds, it should cancel the timeout and carry out the promise chain as normal.
Not really sure how to handle this one.
Use angular's $q together with $timeout. Create a deferred with $q.defer(), create the request with a timeout, and forward the result in the then handler to resolve your deferred. If the request timeouts, start polling. Return the promise of the deferred immediately.
var d = $q.defer() // defered for the final result
function poll() {
$http({...}).then( //poll request
function(res) {
if (ready(res))
d.resolve(res)
else {
$timeout(poll, 10000)
}
},
function(err) {
d.reject(err)
})
}
$http({ timeout: 10000, ... }).then(
function(res) {
d.resolve(res)
}, // return result directly
function(err) { // error or timeout
if (realError(err)) // check if real error
d.reject(err)
else { //timeout
$timeout(poll, 10000)
}
})
return d.promise
You can reuse the returned promise arbitrarily often invoking then to wait for or obtain the cached result.
Under the situation been given I think it is better to use WebSocket rather than timeout function. With WebSocket, you just need to register the function you need to run every time there's an update/change sent from the server. Instead of keeping polling, websocket require less resource and be more efficiency. But it needs a bit of work on back-end.
The implementation defers for different back-end language. You probably need to talk with the back-end people(or yourself). Hope this can give you some idea.
===Edit===
If you want to use service like timeout or interval, the code below should help:
//inside your controller
var pollingPromise = $q.defer(); //used to controll the fired $http request
var pollingActiveUsers = function() {
ReportsResource.getActiveUsers(pollingPromise).then( function(data){
//stop polling
$interval.cancel(pollingProcess);
//set to the controller's scope
$scope.activeUsers = data;
//populate the activeUsersTable
populateActiveUsersTable();
});
};
//init the first request
pollingActiveUsers();
//polling for every 10secs
var pollingProcess = $interval( function() {
//resolve the previous polling request which mean cancel the previous $http request if it waits longer than 10 secs
pollingPromise.resolve();
//start another polling, a new pollingPromise is required
pollingPromise = $q.defer();
pollingActiveUsers();
}, 10000);
//In your service ReportsResource
//you have to make another change function getActiveUsers() to make this work, you have to pass the pollingPromise to the $http method, so you can cancel the $http request:
var function getActiveUsers = function(promiseObj) {
return $http.get('someUrl', { timeout: promiseObj });
};
Few concerns will be taken:
Already fired $http request should be canceled/resolved if it takes more than 10 secs. so the pollingPromise is the one we need. More info here: cancel unresolved promise
a new $http request should be fired every 10 secs, $interval solve this issue and $interval.cancel() function will stop this interval.
polling should stop immediately when receive the desired data
The code might need to be altered when you apply in your app.

Handle Ajax responses in same order as Ajax requests

I would like to secure my code to make sure several Ajax requests triggered by user actions in a certain order will have their responses handled in the same order.
I want to keep the asynchonous mechanism of Ajax. Just secure my code to avoid out of sequence responses that can lead to mess on my web page.
To make it perfectly clear. Let's take an example:
$('#button1').click(function(){
$.ajax({url: 'dosomething1.html'})
.done( function(){ console.log('something 1 success'); } )
.fail( function(){ console.log('something 1 failure'); } );
});
$('#button2').click(function(){
$.ajax({url: 'dosomething2.html'})
.done( function(){ console.log('something 2 success'); } )
.fail( function(){ console.log('something 2 failure'); } );
});
$('#button3').click(function(){
$.ajax({url: 'dosomething3.html'})
.done( function(){ console.log('something 3 success'); } )
.fail( function(){ console.log('something 3 failure'); } );
});
If the user clicks on "#button1" then "#button2" and then "button3", I want to see in the console:
>something 1 success
>something 2 success
>something 3 success
It can happen that the responses are not received in the order the server sent them. So I want to get prepared for this scenario.
Note: I can't know in advance the sequence of events triggered by the user. So I need to find a way to chain the response handlers "on the fly".
What would be the best solution to achieve this?
I'm new to Ajax tricky stuff and I read a large amount of things today without finding THE solution (I guess that somehow deferred and promise objects could do the trick).
Please help me to get rid of this terrible headache. :)
Edit to comment the solution by Kevin B.
I struggled with my brain to FULLY understand the example from Kevin B (that does work) until I read a book on Deferreds that explains that the "then" function is actually creating a new Deferred and returns its promise.
This is this new promise that is "chained" to the previous one. It calls its successfull of failure callbacks depending on the result of the previous promise evaluation (resolved or rejected).
In our case that means that when the previous promise is evaluated, the "then" promise is also evaluated and takes as an input the result (resolved or rejected) of the previous promise to decide which callback to call.
In kevin B's code the ajax request promise is returned in both cases (resolved or rejected).
Hence, the .fail and .done callback of the promise are called ONLY ONCE the "then" promise is evaluated AND the returned promise (ajax request one) is "resolved" (.done function) or rejected (.fail function).
To go further:
My understanding is that the promise is a kind of listener on an event that can potentially happen in the future.
In classical cases, when the event happens, the deferred is changed to "resolved" or "rejected" state and the promise callbacks are called.
The promise is "listening" to the state of the deferred to be changed. The event trigerring this state change is the resolution or rejection of the initial event (ajax request, timeout, other...).
In "then" cases, the trigerring event for evaluating the promise is: the referenced promise (previous promise in the chain) is evaluated (either resolved or rejected).
Given the evaluation result, the success or failure callback is called.
I propose this slightly re-organized code inspired by Kevin's code to help out dummies like me to better understand:
var currentPromise = $.Deferred().resolve().promise();
$('#button1').click(function(){
var button1Promise = $.ajax({url: 'dosomething1.html'})
var thenPromise = currentPromise.then(
function () { return button1Promise;},
function () { return button1Promise;}); // to also handle fail conditions gracefully
currentPromise = thenPromise;
// "thenPromise" callback functions are returning the promise linked to
// the ajax request. So this promise is "replacing" the "thenPromise".
// Hence "done" and "fail" functions are finally defined for the ajax request promise.
thenPromise.done( function(){ console.log('something 1 success'); } );
thenPromise.fail( function(){ console.log('something 1 failure'); } );
});
Hopefully it will help people not totally comfortable with jquery concepts to fully understand promises chaining with "then" function.
Don't hesitate to comment if I misundertood something.
If you create a promise up front, you could keep chaining off of it to get your desired effect.
// ajax mock
function sendRequest(v, delay) {
var def = $.Deferred();
setTimeout(function () {
def.resolve(v);
}, delay);
return def.promise();
}
var ajaxPromise = $.Deferred().resolve().promise();
var delay = 600; // will decrement this with each use to simulate later requests finishing sooner
// think of this as a click event handler
function doAction (btnName) {
delay -= 100;
var promise = sendRequest(btnName, delay);
ajaxPromise = ajaxPromise.then(function () {
return promise;
}).done(function () {
console.log(btnName);
});
}
doAction("1");
doAction("2");
doAction("3");
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
Since i delayed them by 500ms, 400ms, and 300ms, none of them logged until after 500ms.
Here's your code with the same technique:
var ajaxPromise = $.Deferred().resolve().promise();
$('#button1').click(function(){
var promise = $.ajax({url: 'dosomething1.html'})
ajaxPromise = ajaxPromise.then(function () {
return promise;
}, function () {
return promise; // to also handle fail conditions gracefully
}).done( function(){ console.log('something 1 success'); } )
.fail( function(){ console.log('something 1 failure'); } );
});
// repeat for other two buttons
The important thing is all of the ajax requests will always be sent immediately, but the done and fail handlers won't be executed until their turn.
I can't know in advance the sequence of events triggered by the user.
So I need to find a way to chain the response handlers "on the fly".
You need to pipe ajax requests to have the respective responses in the same order, one way to do that is using this plugin https://code.google.com/p/jquery-ajaxq/
As you say:
I can't know in advance the sequence of events triggered by the user. So I need to find a way to chain the response handlers "on the fly".
The right way to go about this is definitely to use deferred objects / promises and NOT set the async parameter to false, which can cause a lot of unwanted problems.
Read the canonical introduction on how to do it here.
EDIT:
An example of synchronizing parallel tasks with $.when(), taken from here:
var promiseOne, promiseTwo, handleSuccess, handleFailure;
// Promises
promiseOne = $.ajax({ url: '../test.html' });
promiseTwo = $.ajax({ url: '../test.html' });
// Success callbacks
// .done() will only run if the promise is successfully resolved
promiseOne.done(function () {
console.log('PromiseOne Done');
});
promiseTwo.done(function () {
console.log('PromiseTwo Done');
});
// $.when() creates a new promise which will be:
// resolved if both promises inside are resolved
// rejected if one of the promises fails
$.when(
promiseOne,
promiseTwo
)
.done(function () {
console.log('promiseOne and promiseTwo are done');
})
.fail(function () {
console.log('One of our promises failed');
});
The easiest way here will be to use async : false parameter for $.AJAX(), just to be sure that your requests run one after another.
http://api.jquery.com/jquery.ajax/

Difference between success,then along with resolve, promise,http in angularjs

In jquery when we fire ajax call ,when its successful,success function is called but, now in angular I have seen people using then as well as success.After googling a bit i found that then returns a promise.
If i want to load a route which has data that comes from a service I use resolve to execute http req before binding data into template but here in http I have used neither success/then.
My http request part :
movieserviceObj.getlist=function(){
return
$http({ url:'app/api/entertainment.php',data:$.param(dataString),method:'POST'});
Resolve part in config:
resolve:{movieslist:moviesController.getallMovies}
Controller :
var moviesController=app.controller('moviesController',function($scope,movieslist){
$scope.movies=movieslist.data.result
});
moviesController.getallMovies=function($q,$timeout,movieservice)
{
var defer=$q.defer();
$timeout(function(){
defer.resolve(movieservice.getlist());
},1000);
return defer.promise;
}
Now above code works completely fine but data-binding occurs after 1 sec as set in $timeout.My issue is that http request gets data within 1 sec, but then too I have to wait for 1sec.Is there any way,that as soon http req is completed it should return promise to resolve till then show loading bar ? Here in http i have not used success / then so how it works.Also even after successful http request how can I make sure that it has key named 'status' in response set to true, if true then only resolve or reject.
$http already returns a promise, so using then() makes much more sense here because:
you won't need to wait 1s every time
you won't risk not getting the data if the requests takes longer than 1s
Here's an example:
var moviesController = app.controller('moviesController',function($scope, movieservice){
movieservice.getlist().then(function(res){
$scope.movies = res.data;
}, function(err){
console.log('error:', err);
});
})
});
There are some nice point about the actual differences between then and success here: Angular HttpPromise: difference between `success`/`error` methods and `then`'s arguments.
Also even after successful http request how can I make sure that it has key named 'status' in response set to true, if true then only resolve or reject.
You'd have to modify your service to do that, similar to what you were doing in a controller:
movieserviceObj.getlist=function(){
var defer=$q.defer();
$http({
url:'app/api/entertainment.php',
data:$.param(dataString),
method:'POST'
}).then(function(res){
if (res.status === true) {
defer.resolve(res);
}
else {
defer.reject({error: 'Status not true'});
}
}, function(err) {
defer.reject(err);
})
return defer.promise;
}
Info about route resolve:
An optional map of dependencies which should be injected into the controller. If any of these dependencies are promises, the router will wait for them all to be resolved or one to be rejected before the controller is instantiated. If all the promises are resolved successfully, the values of the resolved promises are injected and $routeChangeSuccess event is fired. If any of the promises are rejected the $routeChangeError event is fired.

timeout for function (jQuery)

function getNames(){
// some code
}
This function can be done in one second, but sometimes it freezes itself and html block on the page (ajax inside) on infinite time.
I would like to have time limit for this function. If it doesn't finish in ten seconds, then abort it.
How to do this?
This is really easy with jQuery 1.5’s deferred promises.
The following example creates a Deferred and sets two timer-based functions to either resolve or reject the Deferred after a random interval. Whichever one fires first “wins” and will call one of the callbacks. The second timeout has no effect since the Deferred is already complete (in a resolved or rejected state) from the first timeout action.
// Create a Deferred and return its Promise
function asyncEvent() {
var dfd = new jQuery.Deferred();
setTimeout(function() {
dfd.resolve('hurray');
}, Math.floor(Math.random() * 1500));
setTimeout(function() {
dfd.reject('sorry');
}, Math.floor(Math.random() * 1500));
return dfd.promise();
}
// Attach a done and fail handler for the asyncEvent
$.when( asyncEvent() ).then(
function(status) {
alert( status + ', things are going well' );
},
function(status) {
alert( status + ', you fail this time' );
}
);
You can easily modify this example to suit your needs:
// Create a Deferred and return its Promise
function asyncEvent() {
var dfd = new jQuery.Deferred();
// Your asynchronous code goes here
// When the asynchronous code is completed, resolve the Deferred:
dfd.resolve('success');
setTimeout(function() {
dfd.reject('sorry');
}, 10000); // 10 seconds
return dfd.promise();
}
// Attach a done and fail handler for the asyncEvent
$.when( asyncEvent() ).then(
function(status) {
alert( status + ', things are going well' );
},
function(status) {
alert( status + ', you fail this time' );
}
);
If it is the function itself that's grinding away, just set a timer and either return or throw if it exceeds your maximum time period.
If, on the other hand, if the delay is caused by the AJAX request not returning, and you are using $.ajax(), you can always set the timeout setting. It should kill the request. Note that you need to avoid relying on the success callback, since it only gets called if the request succeeds. Instead, use complete callback to watch for failures. It will tell you if an error occurred. According to the documentation, this is available in 1.4 onwards. I'm not sure about pre-1.4, but I think it worked in 1.3 as well.
As suggested above, you can go with Web Workers if //some code is something that might take a long time sometimes (like waiting for large amounts of information from a slow server). That will allow background processing without locking the main page. Note that not all browsers support it yet.
You can find a nice introduction here.

Categories