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?
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.