I am working on a network architecture which gives late response to a GET query. I wish to draw the image once I am able to receive from the server. I am trying to increase delay time of display function so that it can be drawn once fetched from server. I am using canvas to display picture from a particular URI. Here is the portion of code which I need to delay running :
var canvas = document.getElementById('myCanvas');
var context = canvas.getContext('2d');
var img = new Image;
var strDataURI = nameStr;
img.onload = function(){
context.drawImage(img,0,0, 150,150); // Or at whatever offset you like
};
img.src = strDataURI;
Please help. Thanks in advance.
There are several options for this. If you insist on making this a timer then you can use setTimeout().
window.setTimeout(function() { // function code here }, 3000);
You could also set your ajax call to be synchronous instead of asynchronous. This will cause other functions to wait until it is complete before running.
$.ajax({
async: false
});
Finally you could put the draw function in the complete of your ajax call. This function is run after the ajax call is completed.
$.ajax({
complete: function(result) {
// code to perform the draw
}
});
Try setTimeout (reference here http://www.w3schools.com/js/js_timing.asp)
Suggestion
My suggestion is to NOT delay it at all. As chockleyc said, waiting for the response would be the best option.
Cases
There are the following possible scenarios:
You are making the GET request and waiting for the response
You are not making the request manually, and it is simply loaded with page
You make the GET request manually
If you are making the GET query yourself, my strong recommendation is to use Promise like this:
var getRequest = new Promise(function(resolve, reject){
//We call resolve(...) when what we were doing async succeeded, and reject(...) when it failed.
//In this example, we use setTimeout(...) to simulate async code.
var oReq = new XMLHttpRequest();
oReq.onload = function(e) {
resolve(oReq.response);
}
oReq.open("GET", "www.bananas.com");
oReq.send();
});
and then you would use it like:
var getRequest()
.then(console.log);
Which in this case would print the response. If you are not familiar I recommend the MDN Promises documentation.
Another alternative is to simply use the XMLHttp from JavaScript without promises. You can read more examples in the MDN documentation as well or if it is too confusing for you give a try to the kirupa tutorial.
You don't make the GET request manually
In this case, I recommend you listen to the GET request, and then perform a specific action once its response arrives. A good solution for this can be found in this response:
https://stackoverflow.com/a/3597640/1337392
Where you will find a mini library that will listen to ALL GET requests. This way, every time you receive a GET request you can filter it by what you want and execute your code.
If the previous code is too complex for you, you can also have a look at this much simpler alternative:
Listen to serve response JavaScript
You really insist in using a timer
The worst solution by far. Why? try answering these questions:
What happens if the image doesn't arrive before you expect? Instead of 1 second, what if it takes 2?
How do you know for sure exactly how long the GET request will take? Will you make a median of 100 requests? What if you get that one request that is outside the median?
Assuming you always wait the longest possible time (to ensure everything works) why should the majority of your users have a weaker experience?
But if you insist on it, then both answers from "chockleyc" and from "Johannes P" should clarify all your questions.
That's all for now, I hope it helps !
Related
I have some third party library whose events I'm listening. I get a chance to modify data which that library is going to append in the UI. It is all fine until that data modification is synchronous. As soon as I involve Ajax callbacks/promises, this fails to work. Let me put an example to show case the problem.
Below is how I'm listening to a event:-
d.on('gotResults', function (data) {
// If alter data directly it works fine.
data.title = 'newTitle';
// Above code alters the text correctly.
//I want some properties to be grabbed from elsewhere so I make an Ajax call.
$.ajax('http://someurl...', {data.id}, function (res) {
data.someProperty = res.thatProperty;
});
// Above code doesn't wait for ajax call to complete, it just go away and
renders page without data change.
// Yes I tried promises but doesn't help
return fetch('http://someurl...').then(function (data) {
data.someProperty = res.thatProperty;
return true;
});
// Above code also triggers the url and gets away. Doesn't wait for then to complete.
});
I cannot change/alter the third party library. All I have is to listen to event and alter that data.
Any better solutions. Nope. I can't use async/wait, generators, because I want to have it supported for ES5 browsers.
You cannot make a synchronous function wait for an asynchronous response, it's simply not possible by definition. Your options pretty much are:
BAD IDEA: Make a synchronous AJAX request. Again: BAD IDEA. Not only will this block the entire browser, it is also a deprecated practice and should not be used in new code, or indeed ever.
Fetch the asynchronous data first and store it locally, so it's available synchronously when needed. That obviously only works if you have an idea what data you'll be needing ahead of time.
Alter the 3rd party library to add support for asynchronous callbacks, or request that of the vendor.
Find some hackaround where you'll probably let the library work with incomplete data first and then update it when the asynchronous data is available. That obviously depends a lot on the specifics of that library and the task being done.
Does the gotResults callback function really need to return anything else than true? If not, then you could just write regular asynchronous code without this library knowing about it. Let me explain myself by rewriting your pseudocode:
d.on('gotResults', function (data) {
// If alter data directly it works fine.
data.title = 'newTitle';
// Above code alters the text correctly.
//I want some properties to be grabbed from elsewhere so I make an Ajax call.
$.ajax('http://someurl...', {data.id}, function (res) {
data.someProperty = res.thatProperty;
// Above code doesn't wait for ajax call to complete, it just go away and
// EDIT: now it should render properly
renders page without data change.
// Yes I tried promises but doesn't help
return fetch('http://someurl...');
// Above code also triggers the url and gets away. Doesn't wait for then to complete.
}).then(function (data) {
data.someProperty = res.thatProperty;
// maybe render again here?
}).catch(function(err) {
handleError(err); // handle errors so the don't disappear silently
});
return true; // this line runs before any of the above asynchronous code but do we care?
});
I process thousands of points asynchronously in ArcGIS JS API. In the main function, I call functions processing individual features, but I need to finalize the processing when all the features are processed. There should be an event for this, though I didn't find any and I'm afraid it even doesn't exist - it would be hard to state that the last item processed was the last of all. .ajaxStop() should do this, but I don't use jQuery, just Dojo. Closest what I found in Dojo was Fetch and its OnComplete, but as far as I know it's about fetching data from AJAX, not from other JS function.
The only workaround idea I have now is to measure how many features are to be processed and then fire when the output points array reaches desired length, but I need to count the desired number at first. But how to do it at loading? Tracking the data to the point where they are read from server would mean modifying functions I'm not supposed to even know, which is not possible.
EDIT - some of my code:
addData: function (data) {
dojo.addOnLoad(
this.allData = data,
this._myFunction()
);
},
Some comments:
data is an array of graphics
when I view data in debugger, its count is 2000, then 3000, then 4000...
without dojo.addOnLoad, the count started near zero, now it's around 2000, but still a fraction of the real number
_myFunction() processes all the 2000...3000...4000... graphics in this._allData, and returns wrong results because it needs them all to work correctly
I need to delay execution of _myFunction() until all data load, perhaps by some other event instead of dojo.addOnLoad.
Workarounds I already though of:
a) setTimeout()
This is clearly a wrong option - any magic number of miliseconds to wait for would fail to save me if the data contains too much items, and it would delay even cases of a single point in the array.
b) length-based delay
I could replace the event with something like this:
if(data.length == allDataCount) {
this._myFunction();
}
setTimeout(this._thisFunction, someDelay);
or some other implementation of the same, through a loop or a counter incremented in asynchronously called functions. Problem is how to make sure the allDataCount variable is definitive and not just the number of features leaded until now.
EDIT2: pointing to deferreds and promises by #tik27 definitely helped me, but the best I found on converting synchronous code to a deferred was this simple example. I probably misunderstood something, because it doesn't work any better than the original, synchronous code, the this.allData still can't be guaranteed to hold all the data. The loading function now looks like this:
addData: function (data) {
var deferred = new Deferred();
this._addDataSync(data, function (error, result) {
if (error) {
deferred.reject(error);
}
else {
deferred.resolve(result);
}
});
deferred.promise.then(this._myFunction());
},
_addDataSync: function (data, callback) {
callback(this.allData = data);
},
I know most use cases of deferred suppose requesting data from some server. But this is the first time where I can work with data without breaking functions I shouldn't change, so tracking the data back to the request is not an option.
addonload is to wait for the dom.
If you are waiting for a function to complete to run another function deferred/promises are what is used.
Would need more info on your program to give you more specific answers..
I sort of solved my problem, delaying the call of my layer's constructor until the map loads completely and the "onUpdateEnd" event triggers. This is probably the way how it should be properly done, so I post this as an answer and not as an edit of my question. On the other hand, I have no control over other calls of my class and I would prefer to have another line of defense against incomplete inputs, or at least a way to tell whether I should complain about incomplete data or not, so I keep the answer unaccepted and the question open for more answers.
This didn't work when I reloaded the page, but then I figured out how to properly chain event listeners together, so I now can combine "onUpdateEnd" with extent change or any other event. That's perfectly enough for my needs.
My code is structured as follows:
IF (something) {
..stuff
..Asynchronous Function Call
}
ELSE (something) {
..stuff
..Asynchronous Function Call
}
..more stuff
Let's say the IF condition is met, the code executes 'stuff', then moves onto the Asynchronous Function Call. Will it simple do the call but get out of the IF statement and execute 'more stuff' in the mean time on the assumption of waiting for the Asynchronous Function Call to finish?
OR
Does it finish waiting for the Asynchronous Function Call to finish executing, then continue with 'more stuff' as a normal IF statement block would do.
In the prior case, any advice on how to ensure the Asynchronous Function Call finished before it exits the IF block?
** Note, I've included more stuff inside both Asynchronous Function Calls to ensure the calls are done before it moves on, but I feel this is really bad programming because if I had 50 ELIF's, I would have to copy paste that code 50 times as opposed to just putting it at the end of the IF statement.
Thank you very much for any help provided!
You can approach this easily and less painfully using JavaScript Promises. Have a look to the following links:
http://davidwalsh.name/write-javascript-promises
https://www.promisejs.org/
The basic idea of JavaScript Promises is to the use of asynchronous calls that can be executed in a certain order. Like this:
$.when(GET_PRODUCTS).then(
IF_SUCCESS DO THIS
ELSE DO THAT
).fail(
SHOW MESSAGE
CLEAN EVERYTHING BECAUSE SOMETHING WRONG HAPPENED
).done(
CLEAN EVERYTHING BECAUSE EVERYTHING WENT OKAY
)
With that, you can make code that will be more maintainable. It is not easy to grasp it at the beginning, but give it a try, will save you a lot of headaches!
Does it finish waiting for the Asynchronous Function Call to finish executing,
No, that isn't what "asynchronous" means. The while point is that it doesn't wait. The function will run and finish at some point in the future; the flow of execution continues to the next line immediately.
In regards to your given code, more stuff happens while the asynchronous function is happening. It doesn't wait for the asynchronous function to return a result.
Based on your tag of "node.js", I'm assuming your question is about asynchronous calls on the server. However, you can compare the behavior to a client-side AJAX call.
Say you have this:
var nav = document.getElementById('nav');
function async(params) {
var xhr = new XMLHttpRequest();
// set up your request
xhr.onreadystatechange = function() {
// some conditions, and then on success:
nav.style.color = 'black';
};
xhr.open('GET', 'resource.php'+params, true);
// send your request
}
if ( /* condition */ ) {
async( /* some parameter */ );
} else {
nav.style.color = 'red';
}
If you were to run the above code, either way, your #nav element's color will be set to red at first, but if the async request comes back with a successful response, your #nav element's color will be black. This is a very trivial and probably impractical example, but it is one that could be tested pretty easily to confirm that yes, async calls will happen asynchronously.
Like others of said you can use Promises, async.js, step.js, etc. To control flow. You can also use generators if you use latest version of node with --harmony enabled.
I promise to show you the right way. First off asynchronous if conditions are what you're looking for. Secondly you want a full example. You'll have to modify the URL of the AJAX request and set some server code to give you responses. I'll provide a baseline PHP file towards the end.
So effectively: what if my if condition takes too long for the JavaScript parser? JavaScript uses promises. I'm not going to go all-out crazy, my goal here is to provide a baseline. Towards the end of the script you'll notice either success or failure levels. This script requires two asynchronous if conditions. Additionally instead of being cheap/static/fragile I've kept the script element within the head element where it belongs. Lastly ensure you change the HTTP query and acknowledge it at the server, no need to produce redundant files. Browser compatibility is good except no support for IE11 however I've only encountered very specific use-cases to require this so if you're considering using this code for non-technical audience I would highly recommend reconsider your initial approach to the given problem.
<head>
<script defer="true" type="application/javascript">
//<![CDATA[
function request(method,url)
{
return new Promise(function(resolve, reject)
{
var req = new XMLHttpRequest();
req.open(method,url);
req.withCredentials = true;
req.onerror = function() {reject(Error('Network error.'));};
req.onload = function() {if (req.status == 200) {resolve(req.response);} else {reject(Error(new Object({'response':req.response,'status':req.statusText})));}};
req.send();
});
}
function aysn_if()
{
request('get','https://www.example.com/test.php?t=1').then(function(response)
{
console.log('Success, level one if!', response);
request('get','https://www.example.com/test.php?t=2').then(function(response)
{
console.log('Success, level two if!', response);
},
function(error)
{
console.error('Failed, second level.', error);
});
},
function(error)
{
console.error('Failed, first level.', error);
});
}
document.addEventListener('DOMContentLoaded', function (e) {aysn_if();},false);
//]]>
</script>
</head>
PHP
<?php
header('Access-Control-Allow-Credentials: true');
header('Access-Control-Allow-Origin: '.((isset($_SERVER['HTTP_ORIGIN'])) ? $_SERVER['HTTP_ORIGIN'] : '*'));
//JUST for testing, don't send this stuff outside of test environments!
ksort($_SERVER);
print_r($_SERVER);
?>
You first option is what happens.
You don't have to copy/paste N times. Just put "more stuff" into a function, and pass that function to all your asynchronous callbacks. The callbacks can just call the "more stuff" function when they are done with their normal processing.
This is a really basic JavaScript question and probably duplicate, but I don't know the answer!
I have code as follows:
function userlist_change(myval, function_type) {
// relatively slow code involving Ajax call
// based on Ajax results, change some client-side stuff
}
$("#subjectlist").change(function() {
userlist_change($("#subjectlist").val(), 'change');
}).change();
$("#subjectlist").keypress(function() {
userlist_change($("#subjectlist").val(), 'keypress');
});
I have the problem that if the .change() event is called, the userlist_change function kicks off, and it's relatively slow. If the user changes the list again (e.g. by typing), my code waits for userlist_change to complete before restarting it with the new value.
This looks quite odd in the UI, as it can take a few seconds for anything to change client-side - and sometimes the results of the first call only appear after the user has already made a second call.
Is there any way I can interrupt any existing userlist_change process when the .change() or `keypress() event is fired?
[EDIT] What would be ideal is a simple 'kill any running functions with this name' command - is this possible? Or do I really have to fiddle around with timers?!
you can store last request time in a global variable, and store a request time in each ajax request, so that when you are just showing the result of first request, if the global last request time is greater than request, request time, you should show, other wise not. For example:
var lastRequestTime;
function userlist_change(myval, function_type,requestTime) {
// relatively slow code involving Ajax call
// based on Ajax results, change some client-side stuff
if(lastRequestTime <= requestTime){
//show
}
}
$("#subjectlist").change(function() {
lastRequestTime = new Date();
userlist_change($("#subjectlist").val(), 'change',lastRequestTime );
}).change();
$("#subjectlist").keypress(function() {
lastRequestTime = new Date();
userlist_change($("#subjectlist").val(), 'keypress',lastRequestTime );
});
You should use throttling of event. It is quite easily done with RX for JavaScript, but library is quite complicated. You can try filter value with timer.
Here is useful plugin for throttling: http://benalman.com/projects/jquery-throttle-debounce-plugin/
I've got a custom javascript autocomplete script that hits the server with multiple asynchronous ajax requests. (Everytime a key gets pressed.)
I've noticed that sometimes an earlier ajax request will be returned after a later requests, which messes things up.
The way I handle this now is I have a counter that increments for each ajax request. Requests that come back with a lower count get ignored.
I'm wondering: Is this proper? Or is there a better way of dealing with this issue?
Thanks in advance,
Travis
You can store a "global" currentAjaxRequest, which holds the structure of the last XHR request. Then you can abort the current request when you make a new one.
For example:
var currentAjaxRequest = null;
function autoCompleteStuff() {
if(currentAjaxRequest !== null) {
currentAjaxRequest.abort();
}
currentAjaxRequest = $.get(..., function(...) {
currentAjaxRequest = null;
...
});
}
To avoid naming conflicts, wrap that in an anonymous, instantly-executed function, if needed.