Can you help me understand how to fix this asynchronous code? - javascript

I'm new to Javascript and having serious issues trying to understand asynchronous code and how to manage it. My main problem, which kicked this all off, is that I'm trying to read in a JSON object (to quotesList) with an http request and store that in a global for later use. When trying to run my code, because it runs asynchronously, the object will be seen in other functions as undefined since the function defining it has yet to finish by that time. I just don't really know how to resolve this.
Any help is greatly appreciated!
let requestURL = 'https://gist.githubusercontent.com/nasrulhazim/54b659e43b1035215cd0ba1d4577ee80/raw/e3c6895ce42069f0ee7e991229064f167fe8ccdc/quotes.json'
var quotesList;
var x = 5;
var colors = [
"EE6D51",
"72EE51",
"E7EA27",
"FFA428",
"28FF4F",
"456CFC",
"A645FC",
"FC459B",
"FC458A",
"FE2842",
"28FED4"
]
function getQuotes() {
let request = new XMLHttpRequest();
request.open('GET', requestURL);
request.responseType = 'json';
request.send();
request.onload = function() {
quotesList = request.response;
if (quotesList == null) {
alert("Something's definitely wrong here...");
}
console.log('quotesList');
console.log(quotesList);
}
}
function populate() {
var x = Math.floor(Math.random() * Math.floor(quotesList.quotes.length));
document.getElementById('quote').innerHTML = quotesList.quotes[x].quote;
document.getElementById('author').innerHTML = quotesList.quotes[x].author;
}
$(function() {
getQuotes()
populate
while(($('.container strong ').height() >= 300)) {
$('.container strong').css('font-size', (parseInt($('.container strong').css('font-size')) - 10.5) + "px");
$('.container h3').css('font-size', (parseInt($('.container h3').css('font-size')) - 7.5) + "px");
}
});

You should read more about promises, async/await
Here is your working code (this only works on new browsers (no IE for example) otherwise you need babel to transpile the async await code)
let requestURL = 'https://gist.githubusercontent.com/nasrulhazim/54b659e43b1035215cd0ba1d4577ee80/raw/e3c6895ce42069f0ee7e991229064f167fe8ccdc/quotes.json'
var quotesList;
var x = 5;
var colors = [
"EE6D51",
"72EE51",
"E7EA27",
"FFA428",
"28FF4F",
"456CFC",
"A645FC",
"FC459B",
"FC458A",
"FE2842",
"28FED4"
]
function getQuotes() {
return new Promise(function (resolve, reject) {
let request = new XMLHttpRequest();
request.open('GET', requestURL);
request.responseType = 'json';
request.onload = function() {
let status = request.status;
quotesList = request.response;
if (quotesList == null) {
alert("Something's definitely wrong here...");
}
console.log('quotesList');
console.log(quotesList);
if (status == 200) {
resolve(request.response);
} else {
reject(status);
}
}
request.send();
});
}
function populate() {
var x = Math.floor(Math.random() * Math.floor(quotesList.quotes.length));
document.getElementById('quote').innerHTML = quotesList.quotes[x].quote;
document.getElementById('author').innerHTML = uotesList.quotes[x].author;
}
async function start(){
await getQuotes();
populate();
}
$(function() {
start();
while(($('.container strong ').height() >= 300)) {
$('.container strong').css('font-size', (parseInt($('.container strong').css('font-size')) - 10.5) + "px");
$('.container h3').css('font-size', (parseInt($('.container h3').css('font-size')) - 7.5) + "px");
}
});

Related

Chrome Extension Package Size

I need to get the size of the extension package. To do that, I have this code:
chrome.runtime.getPackageDirectoryEntry(function(package) {
package.getMetadata(function(metadata) {
console.log(metadata.size)
}) })
The problem is that it is always 4096. Always. Is this a bug, or am I missing something?
You'll have to enumerate all files in the package and calculate the size, here's an example that works in a background/event page:
function calcPackageSize(callback) {
var totalSize = 0;
var queue = [], xhr = new XMLHttpRequest();
xhr.responseType = "blob";
xhr.onload = function() {
totalSize += this.response.size;
calcFile();
};
chrome.runtime.getPackageDirectoryEntry(function(root) {
var rootDirNameLength = root.fullPath.length;
calcDir(root);
function calcDir(dir) {
dir.createReader().readEntries(function(entries) {
entries.forEach(function(entry) {
if (entry.isFile) {
queue.push(entry.fullPath.substr(rootDirNameLength));
!xhr.readyState && calcFile(); // start the request chain
} else {
calcDir(entry);
}
});
});
}
});
function calcFile() {
if (!queue.length)
return callback && callback(totalSize);
xhr.open("HEAD", queue.pop());
xhr.send();
}
}
Usage:
calcPackageSize(function(size) { console.log(size) });

Maintaining state of variables when changed inside a timer

I am using JavaScript.
I amusing a setInterval timer method.
Inside that method I am changing the values of module variables.
The thing is in IE the changes to the variables are not 'saved'. But in Chrome they are.
What is the accepted practice to do what I need to do?
this is my code:
function start()
{
var myVar = setInterval(function () { GetTimings() }, 100);
}
var currentts1;
var currentts2;
var currentts3;
var currentts4;
var frameCounter;
function GetTimings() {
if (frameCounter < 1) {
frameCounter++;
var xmlhttp = new XMLHttpRequest();
xmlhttp.open("GET", urlTS, false);
xmlhttp.onreadystatechange = function () {
if (xmlhttp.readyState == 4) {
var nextts = xmlhttp.responseText;
var bits = nextts.split('|');
if (currentts1 != bits[0]) {
currentts1 = bits[0];
postMessage("0|" + bits[0]);
}
if (currentts2 != bits[1]) {
currentts2 = bits[1];
postMessage("1|" + bits[1]);
}
if (currentts3 != bits[2]) {
currentts3 = bits[2];
postMessage("2|" + bits[2]);
}
if (currentts4 != bits[3]) {
currentts4 = bits[3];
postMessage("3|" + bits[3]);
}
frameCounter--;
}
}
xmlhttp.send();
}
}
The variables:
currentts1
currentts2
currentts3
currentts4
frameCounter
values are not preserved...
Try this, but notice I changed the currentts* to an Array when you try to view them
function start() {
var myVar = setInterval(GetTimings, 100);
}
var currentts = [null, null, null, null];
var in_progress = 0; // clear name
function GetTimings() {
var xhr;
if (in_progress > 0) return; // die
++in_progress;
xhr = new XMLHttpRequest();
xhr.open('GET', urlTS);
function ready() {
var nextts = this.responseText,
bits = nextts.split('|'),
i;
for (i = 0; i < currentts.length; ++i)
if (currentts[i] !== bits[i])
currentts[i] = bits[i], postMessage(i + '|' + bits[i]);
--in_progress;
}
if ('onload' in xhr) // modern browser
xhr.addEventListener('load', ready);
else // ancient browser
xhr.onreadystatechange = function () {
if (this.readyState === 4 && xhr.status === 200)
ready.call(this);
};
// listen for error, too?
// begin request
xhr.send();
}

XMLHttpRequest loop memory leak

I'm trying to check many items against information on ajax URL. But when I'm running this function in browser, memory usage goes above 2 gigs and then browser crashes (Chrome, Firefox). What am I doing wrong? items variable is really big - >200 000 and also includes some large strings.
var items = [1,2,3,4,5,6,7,8,9,10,...,300000]
var activeItems = {}
function loopAjax(){
for (i=0; i < items.length; i++) {
var currItem = items[i];
var request = new XMLHttpRequest();
var found = 0
request.open("GET", "/item=" + currItem);
request.onreadystatechange = function() {
if (request.readyState == 4 && request.status == 200) {
var response = JSON.parse(request.responseText);
var active = response[0].active;
if (active) {
console.log("FOUND ACTIVE! " + currItem);
activeItems[found] = {"active": true, "item": currItem};
found++;
}
}
}
request.send();
}
}
Thank goodness the browser stalls and dies. If it didn't you just created a denial of service attack!
The problem needs to be reapproached. You better off creating a state machine which has a stack of requests in it. That way you only doing say 5 concurrent requests at a time.
function ItemChecker(sample_size, max_threads) {
this.sample_size = sample_size;
this.max_threads = max_threads;
this.counter = 0;
this.activeItems = [];
this.isRunning = false;
this.running_count = 0;
}
ItemChecker.prototype.start = function start() {
this.isRunning = true;
while (this.running_count < this.max_threads) {
this.next();
}
return this;
};
ItemChecker.prototype.stop = fucntion stop() {
this.isRunning = false;
return this;
};
ItemChecker.prototype.next = function next() {
var request, item_id, _this = this;
function xhrFinished(req) {
var response;
if (req.readyState !== 4) {
return;
}
_this.counter--;
if (req.status === 200) {
try {
response = JSON.parse(request.responseText);
if (response[0].active) {
_this.activeItems.push({
active: true,
item: item_id;
});
}
} catch(e) {
console.error(e);
}
// When finished call a callback
if (_this.onDone && _this.counter >= _this.sample_size) {
_this.onDone(_this.activeItems);
}
}
else {
console.warn("Server returned " + req.status);
}
}
if (!this.isRunning || this.counter >= this.sample_size) {
return;
}
item_id = this.counter;
this.counter++;
request = new XMLHttpRequest();
request.onreadystatechange = xhrFinished;
request.open("GET", "item=" + item_id);
request.send();
};
ItemChecker.prototype.whenDone = function whenDone(callback) {
this.onDone = callback;
return this;
};
This might work? Didn't try it for real. But you would call it with:
var item_checker = new ItemChecker(300000, 5);
item_checker.whenDone(function(active) {
// Do something with active
}).start();

How to wait for all XHR2 send calls to finish

I have done some code to upload multiple files from browser to server, while showing progressbars as well.
XHR2 send calls are asynchronous. The problem is that I want to call some functions after all the XHR2 send calls are finished.
Here is a very simplified snippet of my code:
<input id="upload" multiple="multiple" />
<script>
var files = document.getElementById("upload").files;
for (var i = 0, length = files.length; i < length; i++) {
var file = files[i];
var uploadFunc = function (arg) {
return function () {
processXHR(arg);
};
}(file);
uploadFunc();
}
function processXHR(file) {
var normalizedFileName = getNormalizedName(file.name);
var url = 'upload_file/';
var formData = new FormData();
formData.append("docfile", file);
var xhr = new XMLHttpRequest();
var eventSource = xhr.upload;
eventSource.addEventListener("progress", function (evt) {
var position = evt.position || evt.loaded;
var total = evt.totalSize || evt.total;
console.log('progress_' + normalizedFileName);
console.log(total + ":" + position);
$('#progress_' + normalizedFileName).css('width', (position * 100 / total).toString() + '%');
}, false);
eventSource.addEventListener("loadstart", function (evt) {
console.log('loadstart');
}, false);
eventSource.addEventListener("abort", function (evt) {
console.log('abort');
}, false);
eventSource.addEventListener("error", function (evt) {
console.log('error');
}, false);
eventSource.addEventListener("timeout", function (evt) {
console.log('timeout');
}, false);
xhr.onreadystatechange = function (evt) {
if (xhr.readyState == 4) {
console.log('onreadystatechange: File uploaded.');
}
};
xhr.open('post', url, true);
xhr.send(formData);
}
</script>
Whenever "onreadystatechange: File uploaded." is printed in console, I know that one of the files is done uploading.
But I am not able to write any code that will say "All Files uploaded.".
Thanks for any help.
I am using jquery as well in case some one has a solution using jquery.
Do you know how many calls you are making? If so, at least ajax callback, you check the count. Something like
if (ajaxCallCount === ajaxCallsLength) {
// Do stuff involving post all ajax calls
} else {
ajaxCallCount++;
}
You will need a counter and setInterval method in your case, try this:
<input id="upload" multiple="multiple" />
<script>
var files = document.getElementById("upload").files;
var counter = 0;
for (var i = 0, length = files.length; i < length; i++) {
var file = files[i];
var uploadFunc = function (arg) {
return function () {
processXHR(arg);
};
}(file);
uploadFunc();
}
var interval = setInterval(function() {
if(counter == files.length) {
clearInterval(interval);
console.log("All Files uploaded!");
}
}, 100);
function processXHR(file) {
var normalizedFileName = getNormalizedName(file.name);
var url = 'upload_file/';
var formData = new FormData();
formData.append("docfile", file);
var xhr = new XMLHttpRequest();
var eventSource = xhr.upload;
eventSource.addEventListener("progress", function (evt) {
var position = evt.position || evt.loaded;
var total = evt.totalSize || evt.total;
console.log('progress_' + normalizedFileName);
console.log(total + ":" + position);
$('#progress_' + normalizedFileName).css('width', (position * 100 / total).toString() + '%');
}, false);
eventSource.addEventListener("loadstart", function (evt) {
console.log('loadstart');
}, false);
eventSource.addEventListener("abort", function (evt) {
console.log('abort');
}, false);
eventSource.addEventListener("error", function (evt) {
console.log('error');
}, false);
eventSource.addEventListener("timeout", function (evt) {
console.log('timeout');
}, false);
xhr.onreadystatechange = function (evt) {
if (xhr.readyState == 4) {
counter += 1;
}
};
xhr.open('post', url, true);
xhr.send(formData);
}
</script>

How can I change this variable with ajax?

I'm curious as to why this isn't working, here's the code:
function Ajax(sUrl, fCallback) {
var url = sUrl || '';
var callback = fCallback || function () {};
var xmlhttp = (function () {
if (window.XMLHttpRequest) {
return new XMLHttpRequest();
} else if (window.ActiveXObject) {
try {
return new ActiveXObject("Msxml2.XMLHTTP.6.0");
} catch (e) {
try {
return new ActiveXObject("Msxml2.XMLHTTP.3.0");
} catch (err) {
return new ActiveXObject("Microsoft.XMLHTTP");
}
}
} else {
return null;
}
}());
this.setUrl = function (newUrl) {
url = newUrl;
};
this.setCallback = function (func) {
callback = func;
};
this.request = function (method, data) {
if (xmlhttp === null) { return false; }
xmlhttp.onreadystatechange = function () {
if (xmlhttp.readyState === 4) {
callback(xmlhttp.status, xmlhttp.responseXML, xmlhttp.responseText);
}
};
data = data || '';
data = encodeURIComponent(data);
if ((/post/i).test(method)) {
xmlhttp.open('POST', url);
xmlhttp.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
xmlhttp.send(data);
} else {
var uri = data === '' ? url : url + '?' + data;
xmlhttp.open('GET', uri);
xmlhttp.send();
}
return true;
};
return this;
}
var ajax = new Ajax(''); // sets the url, not necessary for this demonstration
var changed = false;
function change() {
changed = true;
}
function foo() {
ajax.setCallback(change);
ajax.request();
alert(changed);
}
foo();
There is a fiddle here: http://jsfiddle.net/dTqKG/
I feel like the change function would create a closure that would indeed change the changed variable. Does anyone know what's going on?
The ajax.request(); will return before change() is called. That is the async nature of the AJAX calls, and the reason why you need the callback as opposed to just getting return value from send() method.
Other than that there might be some other issues in the code. I question why wouldn't you use one of the many AJAX frameworks readily available instead of writing your own.

Categories