Reputation: 1054
In my project, I need to register a user in the db (POST request) through custom APIs.
Once that operation has been completed, I would like to make a GET request to get all users and pass the newly created information (e.g. id and others) to the next state (I am developing a game with the Phaser framework).
What I am trying to do is to use the callback in the post request to then call the other function that retrieves all the players again, but of course it doesn't work because they are async operations (although I thought I could solve it through callbacks).
These are my 2 functions:
saveNewUser: function(name, password){
var self = this;
if(name && password){
// create new calibration data for the current user
var xhttp = new XMLHttpRequest();
xhttp.open("POST", "https://project.herokuapp.com/api/users",true);
xhttp.setRequestHeader("Content-Type", "application/json;charset=UTF-8");
var input = JSON.stringify({
"name": name,
"password": password,
});
xhttp.onreadystatechange = function() {//Call a function when the state changes.
if(xhttp.readyState == 4 && xhttp.status == 200) {
console.log(xhttp.responseText);
self.retrieveNewUser(name);
}
};
xhttp.send(input);
} else {
self.errorMessage.visible = true;
// make text become not visible again after few seconds
self.time.events.add(Phaser.Timer.SECOND * 3, function () {
self.errorMessage.visible = false;
}, this);
}
}
And the function that gets called inside:
retrieveNewUser: function (name) {
var self = this;
// save user details in the global variables, get user after registration in db
var xhr = new XMLHttpRequest();
xhr.open('GET', 'https://duchennegame.herokuapp.com/api/users', true);
xhr.onload = function () {
var users = JSON.parse(xhr.responseText);
if (xhr.readyState == 4 && xhr.status == "200") {
var userFound = false;
users.forEach(function(user){
if(user.name === name){
// save user details in the global variables
self.game.global.currentUser = user;
self.state.start('welcome');
userFound = true;
}
});
// if user has not been found / wrong password, display error message
if(!userFound){
self.errorMessage.setText('Problem in retrieving the current user');
self.errorMessage.visible = true;
// make text become not visible again after few seconds
self.time.events.add(Phaser.Timer.SECOND * 3, function () {
self.errorMessage.visible = false;
}, self);
}
} else {
console.error(users);
}
};
xhr.send(null);
}
How do you make sure that the GET request is executed only AFTER the POST one has been completed?
EDIT
I realized that this code is never executed, so even the console message is not printed:
xhttp.onreadystatechange = function() {//Call a function when the state changes.
if(xhttp.readyState == XMLHttpRequest.DONE && xhttp.status == 200) {
console.log(xhttp.responseText);
self.retrieveNewUser(name);
}
};
However, in this page they provide an example written in such a way. Is there something I am missing?
Upvotes: 0
Views: 4594
Reputation: 379
Question. Do you need to call retrieveNewUser in order to execute "xhttp.send(input);" ?
If not, I would suggest you wrap the saveNewUser function in a promise. Promises are basically a more advanced form of callbacks that allow you to make asynchronous code execute more synchronously.
So your saveNewUser function would look like this:
saveNewUser: function(name, password){
var self = this;
//Returns Promise Object
return new Promise(function(resolve, reject) {
if(name && password){
// create new calibration data for the current user
var xhttp = new XMLHttpRequest();
xhttp.open("POST", "https://project.herokuapp.com/api/users",true);
xhttp.setRequestHeader("Content-Type", "application/json;charset=UTF-8");
var input = JSON.stringify({
"name": name,
"password": password,
});
xhttp.onreadystatechange = function() {//Call a function when the state changes.
if(xhttp.readyState == 4 && xhttp.status == 200) {
console.log(xhttp.responseText);
//This line of code essentially acts as a return statement
resolve(name);
}
};
xhttp.send(input);
} else {
self.errorMessage.visible = true;
// make text become not visible again after few seconds
self.time.events.add(Phaser.Timer.SECOND * 3, function () {
self.errorMessage.visible = false;
}, this);
}
})};
From there, you can run both functions in a synchronous manner by chaining the promises and running this statement:
saveNewUser(name, password)
.then(function(name){
retrieveNewUser(name);
});
The then function takes a callback as a parameter. The callback receives the data that was resolved (in this case, name) and does something with it (in this case, calls retrieveNewUser).
This ensures that retrieveNewUser() doesn't run until saveNewUser is finished.
Upvotes: 1
Reputation: 114
I would make your HttpRequest (POST request) synchronous.
You currently have xhttp.open("POST", "https://project.herokuapp.com/api/users",true);
which is making the request asynchronous.
If you change the true
parameter to false
, your requests will cause the POST request to hang until complete. Then you may call the Get Request as usual.
Upvotes: 0
Reputation: 424
You might consider using Promise, that way you can have a state after the javascript function complete and then you can call another function.
you may refer to this answer, i think that his example is pretty clear.
function makeRequest (method, url, done) {
var xhr = new XMLHttpRequest();
xhr.open(method, url);
xhr.onload = function () {
done(null, xhr.response);
// CALLBACK ON SUCCESS
};
xhr.onerror = function () {
done(xhr.response);
//CALLBACK ON ERROR
};
xhr.send();
}
// And we'd call it as such:
makeRequest('GET', 'http://example.com', function (err, datums) {
if (err) { throw err; }
console.log(datums);
});`
Please read the whole answer, he explain how to use with Promises, which is a really good practice for JavaScript.
Hands Up 😁👍🏼
Upvotes: 1