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);
Related
I don't understand how the responseHandler function below is receiving a response from an API call, after a payment form submission. Here is a brief summary of the code:
var PaymentThing = function(e) {
this["clientCallback"] = e;
return {
createToken: function() {
// HTTP GET URL is generated and then an HTML script element is appended to the page head
var a = "https://example.com/securitytoken?creditcard=xxxx?callback=PaymentThing.callback";
var f = document.createElement("script");
f.src = a;
document.getElementsByTagName("head")[0].appendChild(f)
},
callback: function(e) {
this["clientCallback"](e.status, e.results)
}
}
}
var responseHandler = function(status, response) {
// How does this even work?
console.log(response, status);
}
// Form Submission
jQuery('#checkout_payment_form').submit(function() {
PaymentThing.createToken(responseHandler);
}
So my question is, how does the responseHandler function receive the response from the HTTP GET call? It looks to me like the HTTP call isn't even being made. I only see a URL being added to an HTML script element (with the createToken function), with the URL never even being called/executed (but somehow it is?). Also, how are the (status, response) parameters passed into the responseHandler function when I don't see them being passed into a responseHandler function call?
I don't understand the logic or order of operations here, can anyone please explain what's going on?
EDIT:
Ok I think I see now how the responseHandler function is receiving the status and response parameters. I edited the code above to show there's a callback function in the generated HTTP url. But still, how is that URL being called? It still looks to me like it's inert and being stuck into the HTML of the page and not activating.
I think if you run the responseHandler function and store the result in a variable that can be called as a parameter in the PaymentThing.createToken() it will take it in consideration.
None of the code there calls responseHandler. There is no point in passing it to the createToken method as it completely ignores all the arguments.
It is possible that the script which is added to the page attempts to call a global function named responseHandler, but there's no way to tell without inspecting the script.
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 am new to javascript, and I am having an issue with a line of code executing before the previous line has finished. From what I understand, I need to create a callback function which will make the last line wait for the previous line to finish. My program takes user input and uses an asynchronous 'post' to send it to a website, and then a message is returned. My original problem occurred when I had the two following lines of code together:
req.send(JSON.stringify(payload))
event.preventDefault();
preventDefault() is not waiting for the send() function to finish. I have tried implementing a callback function, but I am having problems with that as well.
function sendReq(callback){
req.send(JSON.stringify(payload), function(){
callback();
});
}
sendReq(function(){
event.preventDefault();
});
Any suggestions would be much appreciated. Here is my entire code in case that helps.
var apiKey = "appid=fa7d80c48643dfadde2cced1b1be6ca1";
document.addEventListener('DOMContentLoaded', bindButtons);
function bindButtons(){
document.getElementById('dataSubmit').addEventListener('click', function(event){
var req = new XMLHttpRequest();
var payload = {longUrl:null};
payload.longUrl = document.getElementById('inputData').value;
req.open('POST', 'http://httpbin.org/post&' + apiKey, true);
req.setRequestHeader('Content-Type', 'application/json');
var response = JSON.parse(req.responseText);
req.addEventListener('load',function(){
if(req.status >= 200 && req.status < 400){
document.getElementById('outputData').textContent = response.longUrl;
}
else
console.log("Error in network request");
});
function sendReq(callback){
req.send(JSON.stringify(payload), function(){
callback();
});
}
sendReq(function(){
event.preventDefault();
});
});
}
You misunderstand how callbacks work. Check out this article from recurial.com: Understanding callback functions in Javascript
You can't "wait" in JavaScript[*] - due to something called "Run-to-completion" semantics: your code always runs in response to an event (such as "DOMContentLoaded", "click", or XHR's "load") and no more events - and no other event handlers - will be processed until your currently running code finishes (typically by return-ing from the outermost function -- the event handler).
Long-running JS code makes the web page freeze, which is why you were asked to write an asynchronous request in the first place -- synchronous XHR will cause the page to freeze while your code waits for the response from the server.
To avoid the freezes and still get the job done, you can structure your code as a series of callbacks: after you start an asynchronous request you return from the current function and let the system call you back when it has the response. The only moment when you can use the response is in the callback, by the very definition of the callback.
In your case, the callback that indicates that the response is available is the load handler. That's where you should move your var response = JSON.parse(req.responseText); line.
PS. preventDefault() is completely unrelated to your situation, it can't be used to "wait for the send() function to finish"; it's used in cases when you handle an event (such as onsubmit for a <form>) and you want to prevent something that the browser does by default when this event fires (in the <form> example -- well -- submitting the form).
PPS. After you figure out the basics and start writing code that has more than 2 callbacks in a row, look up "promises".
PPPS. [*] Ignore this remark, I added it just to be precise, as it will only confuse you at this stage: You can't "wait" in JavaScript unless you're writing a "generator" and/or using the currently unavailable async/await).
I'm trying to use the same method name for both a function and a method (a function of an object) and calling the function from inside the method. However, the function does not get called at all. These are the relevant bits of the function:
function post(url, data, success, error) {
console.log("Sending"); // This doesn't get even called
var request = new XMLHttpRequest(); // Nor a request is made
// ... (more code here)
}
And the relevant bits of the method
function u(parameter) {
// ... more code here
this.post = function(success, error) { // post request
this.on("submit", function(e) { // Loop through all the nodes
e.preventDefault(); // Stop the browser from sending a request
console.log("Going to send your data"); // This works
post(u(this).attr("action"), u(this).serialize(), success, error); // Post the actual data
console.log("Got here"); // Well it's async, but it also gets here
});
return this;
}
return this;
}
It is supposed to be called from this script when the form is sent:
u("form").post(function(){
alert("Yay, it worked");
}, function(){
alert("Something went wrong");
});
It successfully calls the method and both of the messages are shown. However, the Sending from the function is NOT logged and no request is performed. I'm thinking either a problem with scope or with the function name being overwritten, but I'm not an expert here so any help would be appreciated. Why is the function post() not being called?. There's absolutely no error shown in the console.
After some testing, I can confirm the problem is that they share the name. So, how could I have the same name shared for a method and a function? The method would only be called like u("form").post(callA, callB); and the function like post(url, data, callA, callB)l;
It's your call of the u function that is problematic. You forgot the new keyword, which made the this context be the global scope object - on which you then are overwriting the global post variable.
Two fixes:
Use (new u("form")).post(…); or make your constructor new-agnostic
Don't put the post function in the global scope. Use the module pattern.
I know its a long question, so allow me to explain as best as I can.
I have two javascript functions that I want to run after the page loads, we will call them function1() and function2().
function1() uses AJAX to retrieve information from the database that will arrange the contents in a div from the information obtained in the database. It also returns the contents from the database once the function has finished.
function2() requires the value from the database in order to run properly, so it needs to wait until function1() returns its value before function2() runs. Unfortunately my code is not working, and without going into too much detail, below is a schematic of the code:
function function1() {
if (some_cookie_exists) {
//run some code
} else {
//send parameters through "POST"
xmlhttp.onreadystatechange = function() {
if (xmlhttp.readyState == 4 && xmlhttp.status == 200) {
var a = some_value_from_database;
// change div content
return a;
}
}
//sending parameters
}
function function2(a) {
//run code that depends on value of "a"
}
window.onload = function() {
var data = function1();
function2(data);
The error I get is that var data is undefined. function1() retrieves the data successfully, and runs as I intended it to, but function2() does not execute at all, because the value is missing. Can anyone figure out why I am getting this and how I should go about solving this?
NOTE: I am really only familiar with Javascript (still novice at that), I know essentially nothing about JQuery, so if you do use this to fix the code, please explain to me why this works (it will save me trouble later)
AJAX is asynchronous (that's what the first A stands for). The results of the AJAX operation are not available in function1(), they're retrieved in the onreadystatechange handler that you attach to the XMLHttpRequest object. So it's not clear how you could be doing
var a = some_value_from_database;
in function1().
What you need to do is call function2() from the onreadystatechange handler.
If you post the actual implementation of function1 we may be able to provide more specific details.
UPDATE:
Here's how to call function2() when the value becomes available from the AJAX call:
xmlhttp.onreadystatechange = function() {
if (xmlhttp.readyState == 4 && xmlhttp.status == 200) {
var a = some_value_from_database;
function2(a);
}
}
Do it with a callback.
function function1(callback) {
/* ... */
callback(data); // instead of return
}
var function2 = function(data) { // important: make "function2" a variable so that it can be passed as an argument later on
/* ... */
}
window.onload = function() {
function1(function2); // important: no parentheses after function2, otherwise it would be executed right away
}
Because ajax by definition is asynchronous, function2 will start executing before the ajax completes in function1. JQuery would be useful here, as you can place function2 in the success callback of the ajax call in function1.
So include JQuery in your HTML and the following JS should work:
$(function() { //same as onload
function1() ;
}
function function1() {
$.ajax({
url: "url here",
type: "GET",
success: function(data) { // data is what is returned in ajax response
// rearrange divs
function2(data);
}
});
);
function function2(data) {
}
More on the JQuery ajax function here:
http://api.jquery.com/jQuery.ajax/