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.
Related
I need to display the spinner for at least 2 seconds if ajax has been completed
before transitioning.
I have the following but isnt working
route: {
data(transition) {
this.getDelayedData();
transition.next();
}
},
methods:{
getSliders(){
this.$http.get('/api/sliders/getsliders')
.then(sliders =>{this.sliders = sliders.data
});
},
getPosts(){
this.$http.get('/api/posts/getposts')
.then(posts =>{this.posts = posts.data.data
});
},
getDelayedData(){
function timer() {
var dfd = $.Deferred();
setTimeout(function()
{
console.log("done");
}, 2000,dfd.resolve);
return dfd.promise();
}
$.when( this.getPosts(), this.getSliders(),timer() )
.done();
}
}
I tried to implement the code reading this post
But the $.when function isnt waiting until the setTimeout function is finished executing.
You have already accepted my other answer, but I think the code can be improved significantly, as explained below:
It is generally recommended not to mix jQuery with Vue.js, as both modify DOM:
Vue.js has a very strong hold on the DOM, for its model-view bindings, using its Reactivity System
jQuery may modify using class or id selectors, if you use it for some quick-fix solution somewhere.
You will end up spending a lot of time debugging frontend issues, which takes focus away from the business requirements. Also, Vue.js already requires modern browsers, so technically jQuery is not doing much for browser compatibility - its primary goal.
So if you are open to leaving jQuery behind, here is another method:
getDelayedData() {
// Create a new Promise and resolve after 2 seconds
var myTimerPromise = new Promise((resolve, reject) => {
setTimeout(() => {
// callback function for timer, gets called after the time-delay
// Your timer is done now. Print a line for debugging and resolve myTimerPromise
console.log("2 seconds up, resolving myTimerPromise")
resolve();
}, 2000); // This promise will be resolved in 2000 milli-seconds
});
// Start displaying ajax spinner before fetching data
this.displaySpinner = true;
// Fetch data now, within Promise.all()
Promise.all([myTimerPromise, this.getPosts(), this.getSliders()]).then( () => {
// Hide the spinner now. AJAX requests are complete, and it has taken at least 2 seconds.
this.displaySpinner = false;
console.log("Data fetched for sliders and posts");
});
}
The above code also requires you to have this.displaySpinner, which activates or deactivates your ajax spinner. Or you may have some other way of doing it.
And you still need to return this.$http.get(..) from your getPosts and getSliders methods.
Note: I haven't tested the above code, you need to debug if required. It should work, based on my understanding of Promise.all() as given in this reference doc: https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Promise/all
Once it starts working, you can simplify myTimerPromise as follows:
getDelayedData() {
// Create a new Promise and resolve after 2 seconds
var myTimerPromise = new Promise((resolve, reject) => {
setTimeout(resolve, 2000); // "resolve" is already a function, no need for another anonymous function here
});
// and do the other stuff here...
}
I would prefer this method, as it avoids jQuery. Then my Vue.js app can be as small as 30 KB gzipped, whereas if I bundle jQuery along, it can go well over 100 KB. It is just a personal choice :-)
I believe the problem is caused because your methods - getSliders() and getPosts() are not returning any Promises. So your deferred timer has no way of knowing what is going on in these get methods.
Your this.$http.get(..) from Vue-resource returns a promise. You can return it as follows:
getSliders(){
// Fetch sliders and "return" the promise object
return this.$http.get('/api/sliders/getsliders')
.then(sliders =>{
// The data on vue is already being set here. So it will not wait for timeout.
this.sliders = sliders.data
// Instead you may try to set it on "this.sliders_temp" and then change later to "this.sliders"
});
}, // and so on...
That follows the example given here: http://www.erichynds.com/blog/using-deferreds-in-jquery, which is linked in the other stackoverflow answer that you referred to in the question. They return $.get(..) in their functions.
But as indicated in my code sample above, even if you return this.$http.get(...), it will still not work for you, because you are already setting this.sliders inside your success handler for $http. So your Vue template will update the UI immediately. You need to think through a better strategy for your app.
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.
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/
I wish to validate my assumptions:
As I read yet, to create an async call, I must eventually end up calling a javascript native call (like setTimeout() || XMLHttpRequest() || ...) to get out of the engine main scheduler loop.
Any third party library wishing to be async, will indirectly do the same.
Is this right, or is there a way to create my own async code (for instance by allowing a blocking feature on my code) intentionally without a js native call ?
In JavaScript, there are a few ways to write code that can be run asynchronously. Typically the methods used were:
setTimeout
setInterval
XMLHttpRequest
But in HTML5 (and beyond) there are a lot of other methods that work asynchronously. For example, the new asynchronous file uploading, Web Workers and Promises. (NOTE: not all of these are supported by every browser)
Your question, though, is vague. What are you attempting to do? If it's UI-centric, you may want to look at requestAnimationFrame
If by blocking, you mean you want to show a "loading" gif and then go do stuff and change that after the "stuff" is done, there are a lot of ways to handle that but with the newer browsers, you'd use Promises: http://www.html5rocks.com/en/tutorials/es6/promises/ with a check for some value in a timeout function.
//The value to check
var isDone = false;
var asynchCheck = function(resolve, reject) {
if ( notStarted )
{
// do stuff (calls async function that eventually sets "isDone" = true)
}
if ( isDone === true )
{
if ( no errors ) resolve("Stuff worked!");
else reject(Error("It broke"));
}
//Check again
else setTimeout( asynchCheck, 50 );
}
//Start your function
var promise = new Promise(asynchCheck);
//Now set the success/fail functions
promise.then(function(result) {
console.log(result); // "Stuff worked!"
}, function(err) {
console.log(err); // Error: "It broke"
});
I am having a hard time understanding the value of creating your own Deferred object.
Say you have the following jQuery
function doStuffFirst() {
var dfd = $.Deferred();
// do some stuff here
setTimeout(function() {
dfd.resolve();
},1);
return dfd.promise();
}
function doStuffAfter() {
//do some other stuff
}
$.when(doStuffFirst()).done(doStuffAfter);
I don't actually know that doStuffFirst() has finished, its just waiting some time before firing doStuffAfter()
why is that any better than just going
function doStuffFirst() {
// do some stuff here
}
setTimeout(function() {
//do some other stuff
},1);
You do know that it is finished; but it is useless as it stands, since you're not executing your task asynchronously. The task executes, then the deferred gets created and fired almost immediately. However, if you change to this:
function doStuffFirst() {
var dfd = $.Deferred();
setTimeout(function() {
// do some stuff HERE
dfd.resolve();
},1);
return dfd.promise();
}
then it becomes useful, since it will return immediately, but resolve some time later (whenever the task is done). While you have just one async task, using a deferred is not much different than using a plain callback (just more complex, and prettier, and the dependency goes the other way: callbacks go in, while promises come out of the routine that schedules the task). However, if you have more complex requirements, like having two async tasks which you want to execute simultaneously but wait until both are done, promises are much superior.
Using setTimeout simply executes the code after the given time, whereas promises executes code once the promised task has completed. Promises are used when dealing with things in which completion time is unknown, such as ajax requests. In setTimeout, you know when exactly to execute some code.