I have few functions, which calls another (integrated functions in browser), something like this:
function getItems () {
var xhr = new XMLHttpRequest();
xhr.open("GET", "http://another.server.tld/", true);
xhr.onreadystatechange = function() {
if (xhr.readyState == 4) {
items = $("a[href^=/courses/]", xhr.responseText);
}
};
xhr.send();
}
As I don't want to write more code inside and make each logical functionality separated, I need this function to return variable items.
I know there can happen some accidents (network/server is not available, has long-response...) and the function can return anything after gets data from the server or timeout occurs.
This seems be an async request. I don't think you will be able to return data from this function.
Instead, you can take a callback function as an argument to this function and call that callback when you have the response back.
Example:
function getItems (callback) {
var xhr = new XMLHttpRequest();
xhr.open("GET", "http://another.server.tld/", true);
xhr.onreadystatechange = function() {
if (xhr.readyState == 4) {
callback(xhr.responseText);
}
};
xhr.send();
}
You're asking for synchronous requests: a function should not return until its return value can be computed even if this involves a hundred milliseconds of asking the server for the data.
By setting the third argument of xhr.open to true, you are using asynchronous requests — telling the browser that you want your function to return before the response from the server was received. Which pretty much means that it cannot return the items, since they have not been received yet.
Of course, using synchronous requests is an user interface pain, because your javascript is basically locked up until the response comes back a few hundred milliseconds later (if you're lucky).
What is advised is to correctly architecture your JavaScript code to account for asynchronous communications, and allow values to be returned through a callback instead of return. You don't have to, but it will save you a lot of pain in the end.
Related
I am beginner at JS and was learning AJAX(XHR object) and as you can see the code below. Also, guys if you won't get what I mean please let me know :-)
function easyHTTP() {
this.http = new XMLHttpRequest();
}
// Make an HTTP GET Request
easyHTTP.prototype.get = function(url, callback) {
this.http.open('GET', url, true);
let self = this;
this.http.onload = function() {
if(self.http.status === 200) {
callback(null, self.http.responseText);
} else {
callback('Error: ' + self.http.status);
}
}
this.http.send();
}
const http = new easyHTTP;
http.get('https://jsonplaceholder.typicode.com/posts', function(err, posts) {
if(err) {
console.log(err);
} else {
console.log(posts);
}
});
As you can see the above code uses asynchronous callbacks to handle the retrieved data and then console.log that retrieved data. But I wonder what if instead of using asynchronous callback we will handle the retrieved data and console log it inside onload callback like so:
let self = this;
this.http.onload = function() {
if(self.http.status === 200) {
console.log(self.http.responseText);
} else {
console.log('Error: ' + self.http.status);
}
}
Then just use this code to execute it:
const http = new easyHTTP;
http.get('https://jsonplaceholder.typicode.com/posts')
I just cannot get what is the point of using asynchronous callbacks in XHR when we can wait and handle the retrieved data inside onload callback.
You are probably following some tutorial or where did you get that code?
The reason for supplying a callback to http.get instead of adding the code directly to the onload method is easy: Reusability
What if you want to request from two different URLs and process the data differently? When you embed the processing code into the onload function, you have to implement different get-functions.
The callback on the other hand allows to request from different urls (or even the same) and handle the received data in different ways by specifying different callbacks. This is a typical generalization pattern in javascript.
By the way: The callback is not asynchronous. When onload calls callback, it is called directly, as if the code was at the same place. But what is asynchronous, is the http request itself. It starts executing, when the send method is called. After that javascript just continues executing code. After the request is completed the (anonymous) function assigned to onload is called.
I started studying JS and came across callbacks and promises. I am a little confused, read dozens of sources, but could not exactly find answer. For example a code from mozilla
var xmlhttp = new XMLHttpRequest(),
method = 'GET',
url = 'https://developer.mozilla.org/';
xmlhttp.open(method, url, true);
xmlhttp.onload = function (data) {
console.log(data)
};
xmlhttp.send();
From description of onload :
callback is the function to be executed when the request completes successfully. It receives a ProgressEvent object as its first argument. The value of this (i.e. the context) is the same XMLHttpRequest this callback is related to.
Where does it actually get ProgressEvent from? And how should I differentiate it from
var myFunc = function (data) {
console.log(data);
}
myFunc('ALERT!!!');
Should not I pass parameter to the function?
callbacks are function passed as argument to another function to be executed later.In your case you want to execute something after XMLHttpRequest finish doing its job. So you need to pass a success function to be executed after ajax finish loading it's content.
why we use callback?
after finish loading you might want to do something with the result of http request.You cant do it in the onload handler.
xmlhttp.onload = function(result){
// do stuff with result
}
But you will need to write a lot of code inside the onload event handler.So by passing callback you can keep the workspace clean.Write code for processing the result inside the callback.See the following code
function ajax(callback){
var xmlhttp = new XMLHttpRequest(),
method = 'GET',
url = 'https://developer.mozilla.org/';
xmlhttp.open(method, url, true);
xmlhttp.onload = function (data) {
callback(data) // your callback will execute after http request finish loading
};
xmlhttp.send();
}
function success(result){ // this function will be used as callback
// do stuff with result.
}
// call ajax function and pass the callback function to be executed after loading
ajax(success)
ProgressEvent :
ProgressEvent object contains the data associated with the current request state.The follwoing is your progress event object.See the red underlined properties.loaded is the number of bytes loaded.and total is the total byte to be loaded.This is actually the data size.Thus you can determine how many bytes to be loaded and how many byte actually loaded.If those two properties are similar then the load is complete.That's what onload do.After loading complete both are 191(byte).I have a file that is 191 byte in size.That means the loading is complete.
The callback arguments are provided by whatever part of the code executes the function later. In this case, such code is executed by the browser itself and, being a browser library, there's no JavaScript source code you can inspect.
Perhaps it's easier to understand with a pure JavaScript example:
function printMessageInConsole(message) {
console.log("Message is: %s", message);
}
function doSomeStuff(runThisWhenFinished) {
runThisWhenFinished("I am finished with it");
}
doSomeStuff(printMessageInConsole);
I have a site that makes an HTTP request for JSON data, then a callback function processes the data and displays it by creating a series of divs dynamically. What I want to do is to wait for that function to finish adding the divs to the page, then apply labels only to specific divs created by the previous code.
HTTP Request and Callback
function data(callback){
var url = //request url;
var request = new XMLHttpRequest();
request.onreadystatechange = function() {
if (request.readyState === 4) {
if (request.status === 200) {
document.body.className = 'ok';
//Parse returned string into an object, then pass the object to the callback function.
var data = JSON.parse(request.responseText);
callback(data);
} else {
document.body.className = 'error';
}
}
};
request.open("GET", url , true);
request.send(null);
}
function dataDisplay(data){
//outputs <div id="1064" class="displayed-data">
<p id="message1" class="hidden"></p>
}
data(dataDisplay);
The code above displays my data exactly how I want it to, but when I try to access the numbered ID of the divs I want to change, the function runs before the data is displayed on the page, causing a 'null' error because the data I am trying to change hasn't been added to the DOM yet.
Second Function to change original
function label(){
var message1 = document.createTextNode('//some label');
var displayedData = document.getElementById('1064').getElementById('message1');
displayedData.appendChild(message1);
document.getElementById('message1').classList.remove('hidden');
}
How do I get the second function to wait until the callback has completed before trying to access and change it? I tried a callback inside of a callback, something like: label(data(dataDisplay)); but it still threw the same errors, so I clearly did it wrong. Sorry, I am brand new to JavaScript and don't really know where to go from here.
Thanks for your help!
A pretty quick way of doing it correctly is with this inline function.
data(function(result) {
dataDisplay(result);
label();
});
Be aware that your data function itself completes very quickly - if you need something from its result, you will need to include it in its callback.
I am trying to solve this problem of mine: I am making two ajax calls on window.load, and it seems like second AJAX response rewrite first one before I function can proceed it or I am making something wrong... anyway I am not able to solve it without making calls synchronous and i don't really want to do that for obvious reasons.
So Here is my code (Javascript) To Create new XMLHttpRequest:
function createRequest(){
if(window.XMLHttpRequest)
return new XMLHttpRequest;
else
return new ActiveXObject("Microsoft.XMLHTTP");
}
My send request function (to sending them)
function sendRequest( url, callback){
request = createRequest();
if(typeof callback === 'function'){
request.onreadystatechange = callback();
}
request.open("GET", url, true);
request.send();
}
and finally my callback
function handleData(){
return function(){
if (request.readyState == 4 && request.status == 200) {
serverResponse = JSON.parse(request.responseText);
switch(Object.keys(serverResponse)[0]){
case "a": function1(serverResponse.a,"a"); break;
case "b": function2(serverResponse.b,"b"); break;
}
}
}
I am creating my calls like this:
window.onload = function () {
sendRequest('url' , handleData);
sendRequest('url2', handleData);
}
NOTICE: As you can see, this is simplified version of my code.
In a switch I am calling function1 and 2 these functions are really handling JSON response from server. I am making calls on two different urls on a same domain with different json response.
Also when I make only one call it works well(Both of them)... Problem only occurs when I am trying to make 2 in a row - And when i make two calls only second one is processed right. Thats why i am thinking that second overwrite first.
I tried to make two functions to handleData so there was handleData1() and handleData2() instead of single handleData() with switch so i call them like this:
window.onload = function () {
sendRequest('url' , handleData1);
sendRequest('url2', handleData2);
}
but i run into problem where second ajax response(always the second call) but again only second one succeed. And then i put console.log(request) in both of em, and i get only JSON for second function trying to be processed with second function.(nothing about first one) Sometimes is second function called more times like 4.
Btw handleData2 does not depend on data from handleData1.
If any more questions please do ask :) Thanks
request = createRequest();
creates a global variable. Every call to the function will override that variable, which is what you are experiencing.
Create a local variable and pass the value to the callback:
var request = createRequest();
if(typeof callback === 'function'){
request.onreadystatechange = callback(request);
}
// ...
function handleData(request) {
// ...
}
If I am within my XHR's onreadystatechange function, I can easily do document.title = xhr.responseText, but if I make the function return the responseText, I can't set a variable equal to my XHR's outerlying wrapper to make it equal the response; is there any way to go about doing this?
My wrapper:
ajax = function(url, cb)
{
xhr = (window.XMLHttpRequest)
? new XMLHttpRequest()
: new ActiveXObject('Microsoft.XMLHTTP');
xhr.onreadystatechange = function()
{
if (xhr.readyState == 4 && xhr.status == 200)
{
cb(xhr.responseText);
};
}
xhr.open('get', url, true);
xhr.send();
};
Now, if I did something like:
ajax('bacon.txt', function(_)
{
document.title = _;
}
it works absolutely perfectly; document.title does in fact become the responseText of the call to bacon.txt. If, however, I try to implement it this way:
document.title = ajax('bacon.txt', function(_)
{
return _;
}
no such luck. Can anybody clarify as to why this is?
};
You're assigning the return of the ajax function to the title. The ajax function itself doesn't return anything.
The whole point of AJAX is that the function return immediately it has made the request to the server, before any response is received. You are assigning undefined to the title property (BTW, I usually have my ajax function return the xhr being used allowing me to abort if necessary).
When the request is complete the callback (the cb in your code) function is called, hence your original code makes sense, it does the assigning.
the result of the onreadystatechange function is not the same your "ajax" funcion...
You could make your post sync, and make the ajax function return the same, but it will end up delaying the user response in the browser if the response takes too long.
The better you could do is finding another way of doing that, as you prefer. I mean, don't try to make it work that way because it will be worse.