javascript array empty outside a function - javascript

I have JavaScript code like this:
var buffer=new Array();
function fetchData(min,max){
var ajaxReq = new XMLHttpRequest();
ajaxReq.onreadystatechange = function(){
if (ajaxReq.readyState === 4) {
if (ajaxReq.status === 200) {
buffer= ajaxReq.responseText;
console.log(buffer)//this logs an array to console
} else {
console.log("Error", ajaxReq.statusText);
}
}
};
ajaxReq.open('GET', "server/controller.php?min="+min+"&max="+max, true);
ajaxReq.send();
}
fetchData(1,100);
console.log(buffer);//this log an empty array
two logs with different result, what am I doing wrong? thanks for pointers.

Ajax is asynchronous. That means that console.log(buffer) at the end is executed before the response from the Ajax request.
You should change your method to this:
function fetchData(min,max,callback){
var ajaxReq = new XMLHttpRequest();
ajaxReq.onreadystatechange = function(){
if (ajaxReq.readyState === 4) {
if (ajaxReq.status === 200) {
buffer= ajaxReq.responseText;
callback();
//console.log(buffer)//this logs an array to console
} else {
console.log("Error", ajaxReq.statusText);
}
}
};
ajaxReq.open('GET', "server/controller.php?min="+min+"&max="+max, true);
ajaxReq.send();
}
fetchData(1,100,function(){
console.log("My Ajax request has successfully returned.");
console.log(buffer);
});

You are trying to log() the buffer before the AJAX request in executed. To solve this, your fetchData function needs to handle a callback function.
var buffer=new Array();
function fetchData(min,max, callback){
var ajaxReq = new XMLHttpRequest();
ajaxReq.onreadystatechange = function(){
if (ajaxReq.readyState === 4) {
if (ajaxReq.status === 200) {
buffer= ajaxReq.responseText;
console.log(buffer)//this logs an array to console
if(typeof callback == 'function'){
callback.call(this);
}
} else {
console.log("Error", ajaxReq.statusText);
}
}
};
ajaxReq.open('GET', "server/controller.php?min="+min+"&max="+max, true);
ajaxReq.send();
}
fetchData(1,100, function(){
console.log(buffer);
});
This is the most basic implementation, and will work only if the AJAX response is successful.

This is asynchronous. So your flow goes like this:
call fetchData()
ajax request is sent, registering an onreadystatechange callback
fetchData() completes and returns
buffer is logged out, which doesn't yet contain anything.
Sometime later, the ajax request completes and triggers the callback
The callback puts things in the array.
buffer get's logged out from the callback, and you see it now has items in it.
So you are only starting the asynchronous request once you hit that first console.log. But it actually finishes long afterward.

A couple of issues here. When the ajax call completes the 2nd console.log has already executed before the variable was set.
Also,You're not using the buffer varaible as an Array.

Seems right to me. buffer is empty to start and it doesn't get set until AFTER the asynchronous call is made, so even though you're fetchingData before the second console.log, you're not receiving it until after it shows an empty buffer.

MDN: https://developer.mozilla.org/en/XMLHttpRequest
void open(in AUTF8String method, in AUTF8String url, in boolean async, in AString user, in AString password);
The third argument is used to tell the browser whether the request should be made asynchronous or not. You set it to true, thus it will be async.
Async basically means that the request is sent and meanwhile other code is executed. So, it starts the request, and while waiting for a response, it logs the buffer: before the request has finished. If you want to log the contents, do it in the onreadystatechange event or set the third argument (async) to false.

Related

I would like to execute regular function in async way

Consider this sample (say this is module)
function Calculator(value){
return {
add: function(value2){
return: {
value: function(){
return value + value2;
}
}
}
}
}
This is a class, and requires an argument when initialization, sample usage:
var Calculator = require('calculator_app_module');
var myCalc = new Calculator(1); // initialized with 1
myCalc.add(2).value(); // === 3;
Which is obviously expected, what i want is to execute add function in async way, just like that
var Calculator = require('calculator_app_module');
var myCalc = new Calculator(1); // initialized with 1
myCalc.add(2).value() ==== 3 // this executes in 2secs (async)
// and then returns result
I would like to patch Calculator.add method so that it can work with async
function patch(module){ //module is Calculator class
var oldAdd = Calculator.add;
Calculator.add = function(){
// some magic
// trigger event or whatever
oldAdd.apply(Calculator, arguments);
}
}
INDEX.JS
var Calculator = require('calculator_app_module');
var calc = new Calculator(1);
calc.add(2).value() === 3; // equalize within 2 seconds
// after async call is done
calc.add(2).value().equal(3); // also legit
The problem is that calc.add(n) returns new function value which is undefined in async call, is there a way to get the calling fn of add and call it back when result comes
update
Prior to #Zohaib Ijaz answer, you cannot modify content/logic of package, only extend/patch, Package must return same API but in promise way, no code breaking
calc.add(2).value() === 3; // sync code
calc.add(2).value() === 3; // async code
calc.add(2).value().equal(3); // async code
How to achieve
update
According to #Zohaib Ijaz comment, this also legit
myCalc.add(2).value().equal(3); //async
Point is in converting sync to async without breaking package, but extending the outcome
If you request a result by calling a chain of methods, like this:
a = myCalc.add(2).value();
or this:
myCalc.add(2).value().equal(3);
then there is no possibility to retrieve and use results that become available only asynchronously (i.e. later, after the statement has been evaluated). Note that asynchronous involves some event being put in the event queue. The currently executing code must finish first (i.e. until the call stack is empty), before that event can get processed.
The above syntax is useful for immediate evaluation only. In order to process asynchronous results you need to provide a call-back function somewhere for being informed about those results.
So with an asynchronous dependency in the add method, your code could provide a callback to the add method, which it would call when it has received the asynchronous result:
myAsyncCalc.add(2, function (added) {
a = added.value();
});
Or, when using promises (which is really nice to work with), the add method would return an object to which you can assign the same call-back:
myAsyncCalc.add(2).then(function (added) {
a = added.value();
});
Note that the callback function is not part of the currently executing code. It just is a function reference, that can be used at a later, asynchronous event for calling you back. But that will be part of a separate execution sequence, that only starts when the internal event queue has been processed and an event has been processed that triggered that execution sequence.
If this is not an acceptable solution, and you really need the former syntax to somehow take an asynchronous produced result into account, then you are without hope: it is not possible, because that really represents synchronous code execution.
Wrapping your Object
You write that you cannot modify the content of the package, but can only extend it.
One way to do that is to make use of proxies.
The idea is that you trap a reference to the add method, and return
your own adapted version of the method, which can optionally still call the original method.
See the above referenced MDN article for examples.
Calling HTTP request Synchronously
If you really want to write code like this:
a = myCalc.add(2).value();
even when the implementation of add performs an HTTP request, then you could have a look at making the HTTP request synchronously. But it should be noted that this is considered bad practice.
Code Example
Here is code that performs the addition in three ways:
unmodified (synchronous)
with an asynchronous HTPP call
with a synchronous HTTP call
For the two modified versions, a proxy pattern is used. For the asynchronous example, a call back is used using the Promise pattern.
Code:
// code in module is not modified
function Calculator(value){
return {
add: function(value2){
return {
value: function(){
return value + value2;
}
}
}
}
}
// standard object creation
var myCalc = new Calculator(1); // initialized with 1
// Create a proxy for the above object, which will expose
// an asynchronous version of the "add" method. Note that the
// "myCalc" object is not modified.
var myCalcHttpAsync = new Proxy(myCalc, {
get: function(myCalc, name) {
if (name !== 'add') return myCalc[name]; // pass-through
return function(value2) {
return new Promise(function(resolve, reject) {
// Define some url
var url = 'http://api.stackexchange.com/2.2';
// Perform HTTP request
var request = new XMLHttpRequest();
// Define call back for when response becomes available
request.onload = function() {
if (request.readyState !== 4) return;
// When async task notifies it has finished:
// call the original "add" method and notify those
// waiting for the promise to get resolved
resolve(myCalc.add(value2));
};
// `true` as third argument makes the request asynchronous
request.open('GET', url, true);
request.send(null);
});
};
}
});
// Create another, alternative proxy for demonstrating
// synchronous HTTP call:
var myCalcHttpSync = new Proxy(myCalc, {
get: function(myCalc, name) {
if (name !== 'add') return myCalc[name]; // pass-through
return function(value2) {
// Define some url
var url = 'http://api.stackexchange.com/2.2';
// Perform HTTP request
var request = new XMLHttpRequest();
// `false` as third argument makes the request synchronous
request.open('GET', url, false);
// code execution "hangs" here until response arrives
request.send(null);
// process response...
var data = request.responseText;
// .. and return the value
return myCalc.add(value2);
};
}
});
// I/O
var std = document.getElementById('std');
var async = document.getElementById('async');
var sync = document.getElementById('sync');
// 1. Standard
std.textContent = myCalc.add(2).value();
// 2. Asynchronous HTTP
myCalcHttpAsync.add(2).then(function (added) {
// This needs to happen in a callback, otherwise it would be synchronous.
async.textContent = added.value();
});
// 3. Synchronous HTTP
sync.textContent = myCalcHttpSync.add(2).value();
Unmodified result: <span id="std">waiting...</span><br>
Result after asynchronous HTTP call: <span id="async">waiting...</span><br>
Result after synchronous HTTP call: <span id="sync">waiting...</span><br>
Here is my solution using promise.
Here is a link to jsbin where you can execute the code.
http://jsbin.com/qadobor/edit?html,js,console,output
function Calculator(value) {
return {
add: function(value2) {
return new Promise(function(resolve, reject) {
setTimeout(
function() {
resolve(value + value2);
}, 2000);
});
}
};
}
var myCalc = new Calculator(1); // initialized with 1
myCalc.add(2).then(function(ans){
// this callback will be called after 2 seconds after promise resolve.
console.log(ans);
});

Getting value from Ajax request in variable

I've this function.
function ajaxtakesource4(callback){
var ajaxRequest; // The variable that makes Ajax possible!
try{
ajaxRequest = new XMLHttpRequest();
} catch (e){
try{
ajaxRequest = new ActiveXObject("Msxml2.XMLHTTP");
} catch (e) {
try{
ajaxRequest = new ActiveXObject("Microsoft.XMLHTTP");
} catch (e){
alert("Your browser broke!");
return false;
}
}
}
// Create a function that will receive data sent from the server
ajaxRequest.onreadystatechange =function(){
if(ajaxRequest.readyState == 4 &&ajaxRequest.status==200){
var sourcetest = ajaxRequest.responseText;
callback(sourcetest);
}
}
ajaxRequest.open("POST", "takesource4.php", true);
ajaxRequest.send(null);
}
Also:
var somous4;
function run() {
ajaxtakesource4(function(sourcetest){
somous4=sourcetest;
});
alert(somous4);
}
and here I call the above the function:
<div id="run">
<button id="button_run" class="button" onclick="run()">Run</button>
</div>
When I click on the button it's supposed to alert response from Ajax request, but looks to be alerting a falsy value (undefined), as seen in this line:
alert(somous4);
I suggest you change the callback in the run function as follows:
var somous4;
function run() {
ajaxtakesource4(function(sourcetest){
somous4=sourcetest;
alert(somous4);
});
}
You're alerting somous4 before it's changed by the request callback. In this case the commands block executes first than the request callback.
Server-side languages as PHP does the work automatically, so you don't need to use events there. It sleeps while the request is not done. That's because the commands block turns the event callback.
Asynchronous code executes concurrently in nature. Thus, your alert statement may execute before your callback executes (callback will only after it receives back data from server). Put the alert inside the callback and it will show the value returned i.e.
var somous4;
function run() {
ajaxtakesource4(function(sourcetest){
somous4=sourcetest;
alert(somous4);
});
}
Edit: Based on OP's comment, instead of thinking about return values, do this:
function foo(soumous4) { // use somous4 for whatever you want... }
// Call this function foo inside the callback.
ajaxtakesource4(function(sourcetest){
somous4=sourcetest;
foo(somous4);
});

How to delay execution of javascript function until JSON has loaded

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):
template.html
<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:
If I inspect the data in the browser dev console, it's all there.
If I just paste the code from the file in the console, the page draws fine.
If I hard-code the initializing arrays etc for the data manipulation part of the code (to get rid of the can't convert undefined, then the page draws but all the graphics indicate that they were populated with no data.
The page loads fine if I put the the JSON data in a .js file and load it as a script just before the createPage.js file at the end of the body.
I inserted a 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.
Why? Are my Promises incorrect?
How can I fix this?
Promise.prototype.then() expects 2 arguments(onFulfilled & onRejected) as function-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)
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?

How can I make a jquery $.when statement refire if it fails?

So sometimes due to some kind of server asynchronicity issue, or something.. I don't know what, but it only happens every once in a while (s_mcir_2 calls its own AJAX functions that are cross-domain, so the other server I suppose can be unreliable).. Anyway, every once in a while result is returned from s_mcir_2 as null instead of a JSON object.
When this happens, I would like to test if it is null, and then if it is, have the $.when statement refire.. theoretically until it receives valid output.
Any ideas?
$.when(s_mcir_2(alt, data[l_alt])).then(function(result) {
//EVALUATE "result"
});
Deferred objects can only resolve once.
Try using a named function that calls itself on fail.
function myFn () {
$.when(s_mcir_2(alt, data[l_alt])).then(function(result) {
//EVALUATE "result"
if (!result) {
setTimeout(myFn,125);
log("R_MCIR_2 has failed");
}
else {
// success, do stuff
...
}
});
}
.then() can take two callback functions, one for done and one for fail. If you run your code inside a function you can recursively re-run your AJAX request (note that this could create an infinite loop):
var failedCount = 0;
function some_func() {
$.when(s_mcir_2(alt, data[l_alt])).then(
//done callback
function(result) {
//EVALUATE "result"
},
//fail callback
function () {
failedCount++;
if (failedCount < 10) {
//try again
some_func();
}
});
}
Docs for .then(): http://api.jquery.com/deferred.then
This code assumes that the s_mcir_2() function returns a jqXJR object (e.g. return $.ajax(...)).
Notice I added a counter to the failed function so this process won't continue infinitely. Once 10 requests have been made, the recursiveness stops.
UPDATE
If instead of the jqXHR object being rejected, if it is resolving to success you can check the server response and if it is null (e.g. typeof(serverResponse) == 'null') then re-run the some_func() function.
function attempt(){
$.when(s_mcir_2(alt, data[l_alt])).then(function(result) {
//EVALUATE "result"
}).fail(function(result) {
log("R_MCIR_2 has failed");
setTimeout(attempt,500)
});
}

How can I make XHR.onreadystatechange return its result?

I'm new to JavaScript programming. I'm now working on my Google Chrome Extension. This is the code that doesn't work... :P
I want getURLInfo function to return its JSON object, and want to put it into resp. Could someone please fix my code to get it work?
function getURLInfo(url)
{
var xhr = new XMLHttpRequest();
xhr.open
(
"GET",
"http://RESTfulAPI/info.json?url="
+ escape(url),
true
);
xhr.send();
xhr.onreadystatechange = function()
{
if (xhr.readyState == 4)
{
return JSON.parse(xhr.responseText);
}
}
}
var resp = getURLInfo("http://example.com/") // resp always returns undefined...
Thanks in advance.
You are dealing with an asynchronous function call here. Results are handled when they arrive, not when the function finishes running.
That's what callback functions are for. They are invoked when a result is available.
function get(url, callback) {
var xhr = new XMLHttpRequest();
xhr.open("GET", url, true);
xhr.onreadystatechange = function () {
if (xhr.readyState == 4) {
// defensive check
if (typeof callback === "function") {
// apply() sets the meaning of "this" in the callback
callback.apply(xhr);
}
}
};
xhr.send();
}
// ----------------------------------------------------------------------------
var param = "http://example.com/"; /* do NOT use escape() */
var finalUrl = "http://RESTfulAPI/info.json?url=" + encodeURIComponent(param);
// get() completes immediately...
get(finalUrl,
// ...however, this callback is invoked AFTER the response arrives
function () {
// "this" is the XHR object here!
var resp = JSON.parse(this.responseText);
// now do something with resp
alert(resp);
}
);
Notes:
escape() has been deprecated since forever. Don not use it, it does not work correctly. Use encodeURIComponent().
You could make the send() call synchronous, by setting the async parameter of open() to false. This would result in your UI freezing while the request runs, and you don't want that.
There are many libraries that have been designed to make Ajax requests easy and versatile. I suggest using one of them.
You can't do it at all for asynchronous XHR calls. You cannot make JavaScript "wait" for the HTTP response from the server; all you can do is hand the runtime system a function to call (your handler), and it will call it. However, that call will come a long time after the code that set up the XHR has finished.
All is not lost, however, as that handler function can do anything. Whatever it is that you wanted to do with a return value you can do inside the handler (or from other functions called from inside the handler).
Thus in your example, you'd change things like this:
xhr.onreadystatechange = function()
{
if (xhr.readyState == 4)
{
var resp = JSON.parse(xhr.responseText);
//
// ... whatever you need to do with "resp" ...
//
}
}
}
For small edit talking about post: https://stackoverflow.com/a/5362513/4766489
...
if (typeof callback == "function") {
//var resp = xhr.responseText;
var resp = JSON.parse(xhr.responseText);
callback(resp);
}
...
And when you call
...
function(data) {
alert(data);
/* now do something with resp */
}
...

Categories