Reputation: 70
I have a JS script to perform service call, which might load some data and then put it into localStorage. Unfortunately, because of some async issues, my localStorage is empty when I try to access it. JS is not my primary language and I don't have deep understanding on how async calls work, so I want to learn from my current example.
JS code to perform request:
function getRM() {
var handleResponse = function (status, response) {
localStorage.setItem('return_matrix', response);
}
var http=new XMLHttpRequest();
var handleStateChange = function () {
switch (http.readyState) {
case 0 : case 1 : case 2 : case 3 : break;
case 4 : // COMPLETED
handleResponse(http.status, http.responseText);
break;
default: alert("error");
}
}
http.onreadystatechange=handleStateChange;
http.open("GET",'{% url 'returnMatrix' %}', true);
http.setRequestHeader('Content-type', 'application/json');
http.setRequestHeader('X-CSRFToken', '{{ csrf_token }}');
http.send(null);
}
JS code to handle local storage item applied to window.onload:
function createTableData() {
if (localStorage.getItem('return_matrix') === null) {
getRM()
}
var returnMatrix = JSON.parse(localStorage.getItem('return_matrix'));
//...
/*save data to locat storage*/
returnMatrix['years'] = years; // here I get an error that returnMatrix is null
returnMatrix["present_value"] = sum;
returnMatrix["actual_contributions"] = actualContributions;
localStorage.setItem('return_matrix', JSON.stringify(returnMatrix))
//...
}
Upvotes: 0
Views: 166
Reputation: 84912
The simplest way to resume code after an async thing is with a callback function. That would look something like this:
function getRM(callback) { // <--- accepts a callback param
const handleResponse = function (status, response) {
localStorage.setItem('return_matrix', response);
callback(); // <--- calling the callback
})
const http=new XMLHttpRequest();
// ...etc
}
And it would get used something like this:
function createTableData() {
if (localStorage.getItem('return_matrix') === null) {
getRM(doWork);
} else {
doWork();
}
}
// I split this out into a helper function because it sometimes needs to be
// called synchronously, sometimes asynchronously, and i didn't want to
// duplicate the code.
function doWork() {
const returnMatrix = JSON.parse(localStorage.getItem('return_matrix');
//... etc
}
Callbacks work, but can be somewhat combersome when you want to chain them together or deal with errors. Another common technique which improves on this is Promises. A promise is an object that represents an eventual value. You can get access to that eventual value by calling the promise's .then method, and providing a callback.
Many libraries for doing http requests (eg, axios, fetch) will return a promise, and so you can work with them without doing anything extra. In your example though you manually did an XHR, and this doesn't have promises built in. But you can still add them if you want, something like this:
function getRM() {
// creating a promise object
return new Promise((resolve, reject) => {
const handleResponse = function (status, response) {
localStorage.setItem('return_matrix', response);
resolve(); //<--- calling resolve instead of callback
}
const http = new XMLHttpRequest();
// ...etc
});
}
And you'll use it like this:
function createTableData() {
if (localStorage.getItem('return_matrix') === null) {
getRM().then(doWork);
} else {
doWork();
}
}
Now that the code is using promises, one further refinement we can do is to use async/await, which is a syntax for working with promises a little easier. That version would look like this:
async function createTableData() {
if (localStorage.getItem('return_matrix') === null) {
await getRM();
}
// I've moved the code back in line, since it's no longer needed in 2 places
const http=new XMLHttpRequest();
//... etc
}
And now it's back to looking pretty much like what you originally had, except getRM now returns a promise, and createTableData will await the resolution of that promise.
Upvotes: 1