Reputation: 5776
I have a page that chains two API calls, loads the data into first_data
and second_data
before executing a createPage
function (which is several kb of data manipulation and d3.js):
<script src="createPage.js"></script>
<script>
var first_data, second_data = [], [];
function getFirstData(){
return new Promise(function(resolve) {
var xhr = new XMLHttpRequest();
var url = "/API/my-request?format=json"
xhr.onreadystatechange = function() {
if (xhr.readyState == 4 && xhr.status == 200) {
first_data = JSON.parse(xhr.responseText);
resolve('1');
}
}
xhr.open("GET", url, true);
xhr.send();
});
} //similar function for getSecondData()
getFirstData()
.then(getSecondData)
.then(createPage(first_data, second_data));
</script>
The trouble is that some of the code that manipulates the data in createPage
is showing errors, for example "can't convert undefined to object
". In that particular error's case, it's because I try to do Object.keys(data[0])
on some data that should be loaded from the API requests. Some observations:
can't convert undefined
, then the page draws but all the graphics indicate that they were populated with no data.console.log("starting")
statement at the start and end of createPage()
. Looking at the network and js console output when I load, the starting
output occurs before the two API GET requests are displayed in the network activity. Is this representative of what's really happening (i.e. can you mix javascript console and network console timing?)So, clearly I don't have access to the data at the point when I need it.
Promises
incorrect?Upvotes: 0
Views: 1219
Reputation: 1465
In functional programming and using promises, you should probably refactor getFirstData (and getSecondData) to the following form:
function getFirstData(){
return new Promise(function(resolve, reject) {
var xhr = new XMLHttpRequest();
var url = "/API/my-request?format=json"
xhr.onreadystatechange = function() {
if (xhr.readyState == 4 && xhr.status == 200) {
// Resolve the result, don't assign it elsewhere
resolve(JSON.parse(xhr.responseText));
} else {
// Add rejection state, don't keep the promise waiting
reject("XHR Error, status = ", xhr.status);
}
}
xhr.open("GET", url, true);
xhr.send();
});
}
And then resolve the promises like this (assume first & second data is not dependant on each other)
Promise.all([getFirstData(), getSecondData()]).then(function(allData){
var first_data = allData[0];
var second_data= allData[1];
return createPage(first_data, second_data);
}).catch(function(error){
console.log("Error caught: ", error);
});
To make things even cleaner, you can change createPages's from:
function createPage(first_data, second_data){
// Function body
}
to
function createPage(data){
var first_data = data[0];
var second_data= data[1];
// Function body
}
and the Promise part:
Promise.all([getFirstData(), getSecondData()]).then(createPage);
Notice how short it became?
Upvotes: 1
Reputation: 36609
Promise.prototype.then()
expects 2 arguments(onFulfilled & onRejected
) asfunction-expression(OR handler or callback)
as it is a function(handler) which will be invoked when Promise is fulfilled
In your case, createPage(first_data, second_data)
will invoke the function createPage
when statement is interpreted by interpreter.
Use anonymous function
as an argument and invoke your function inside it.
getFirstData()
.then(getSecondData)
.then(function() {
createPage(first_data, second_data);
});
Edit: If you are not passing any arguments specifically to the callback, you can use .then(FUNCTION_NAME)
Upvotes: 1