Javascript array length always is 0 - javascript

I tried map elements to [] for angular. But if I checked length of objects there is always 0..
var objects = [];
this.get().subscribe(
response => {
for (var i = 0; i < response.length; i++) {
objects.push(response[i]);
}
}
);
console.log(objects.length);
screen if I console.log(objects)
What I am doing wrong?

You are doing console.log before getting response in subscribe. Just move console.log inside subscribe.
var objects = [];
this.get().subscribe(
response => {
for (var i = 0; i < response.length; i++) {
objects.push(response[i]);
}
console.log(objects.length);
}
);

This is due to the asynchronous nature of execution of the code. The subscribe method accepts a callback, which is the arrow function that you have written with the parameter named response.
So, the callback will not be executed immediately but will be done after a while. But, since JS is asynchronus, it wouldn't wait for the callback to get executed and will move on to the next line of the code where the variable will still be an empty array.
As suggested in the other answers, you could put the console log within the callback function to log the expected value.

You are logging something that's set later so the preview shows an empty array but you can expand it.
The log will show the item as it is right now; not as it is when you log it.
var arr = [];
console.log(arr);//shows [] but when you expand it it shows items
arr.push({name:'john'});
If you want to see an object's values at the time you are logging the object and plan to mutate it after the log then you can do the following:
console.log(JSON.stringify(object,undefined,2);
Your objects.push(response[i]); statement is in a callback and that callback is probably called after your console.log(objects.length); you can proof that by adding another log:
var objects = [];
this.get().subscribe(
response => {
console.log('second log')
for (var i = 0; i < response.length; i++) {
objects.push(response[i]);
}
}
);
console.log("first log",objects.length);
If you have a value that resolves once at a later time then you could use Promises but it looks like you subscribe to an event that can resolve multiple times so your response will only be available in the callback.

Seems that subscribe code is async, so you can wrap this up on a promise. result is the objects you want
const promise = new Promise((resolve, reject) => {
var objects = [];
this.get().subscribe(response => {
for (var i = 0; i < response.length; i++) {
objects.push(response[i]);
}
resolve(objects);
});
});
promise.then(result => console.log(result));

Related

Javascript asynchronous behavior for loops

I was fetching data asynchronously using async/await and then I am running two for loops one after another. So my question is will the for loops overlap each other for big data sets as js is asynchronous and if yes how to solve this?
And for what condition loops can overlap?
Actually, I am trying to make a dropdown and its working but I had this doubt.
const createDropdown = async language => {
let i = 0;
let listPromise = new Promise((resolve, reject) => {
db.ref("Products/" + language).on('value', snapshot => {
resolve(snapshot.val())
})//Fetching Data
})
let list = await listPromise;
for(i in list)
dropdown.remove(0)
for(i in list)
dropdown.options[dropdown.options.length] = new Option(list[i].name, list[i].name)
}
I am running this code and the for loops are not getting overlapped but is there a condition that it will?
Loops which are put in the code one after the other will never overlap either the code inside the loops are synchronous or asynchronous.
for (var i = 0; i < 10; i++) {
doSomethingSync()
}
for (var j = 0; j < 10; j++) {
createPromise().then((res) => {console.log(res))
}
for (var k = 0; k < 10; k++) {
var res = await createPromise();
console.log(res);
}
Above, the "I" loop completes all its operations and then the "J" loop, and then the "K" loop.
Here is the order and the details of each operation
The "I" loop serializes the 10 synchronous operations.
The "J" loop create 10 different promises. They maybe resolved in a different order.
The "K" loop creates 10 serialized promises. Each iteration waits until the promise is resolved before going for the net one.
1, 2, and 3 always happen one after the other.
I used this solution to work with axios, with about 1000 requests to a server and works fine, maybe this can be your solution too. In this code you will make a promise of your db.ref and wait for the response then you use that to manipulate.
const createDropdown = async (language) => {
const listPromise = await Promise.all(
return await db.ref("Products/" + language).on ('value', snapshot => snapshot.val())
)
for(i in listPromise)
dropdown.remove(0)
for(i in listPromise)
dropdown.options[dropdown.options.length] = new Option(list[i].name, list[i].name)
}

Looping a javascript promise to append to an array

Good day, below I have
var request = require('request');
getFood = function(lst){
return new Promise(function(resolve, reject){
//console.log(lst)
request('https://api.jamesoff.net/recipe', function (error, response, recipe) {
lst.push(JSON.parse(recipe))
resolve(lst);
})
});
}
getFood([]).then(function(data){
for(var i = 0; i < 3; i++){
getFood(data)
}
return(data);
}).then(function(data){
console.log(data)
})
What I am aiming to accomplish is by using a promise chain place three recipies into an array using a loop. I only get one item and I know why this occurs, however I can't wrap my head around the procedure to accomplish my goal. Could I please get an explanation with code?
The problem is your getFood call in a loop. If you are looping through promises the best way to do it is with Promise.all()
An example:
var promiseArray = [];
for(var i = 0; i < 3; i++){
promiseArray.push(getFood(data))
}
Promise.all(promiseArray).then(function(values) {
// values is array of resolve values in the order that promises were pushed into the array
});
This resolves the promises in the order in which they were called and should help with your issues.

Using closure with a promise in AngularJS

I don't have a lot of experience with JavaScript closures nor AngularJS promises. So, here is my scenario
Goal:
I need to make $http requests calls within a for loop
(Obvious) problem
Even though the loop is done, my variables still have not been updated
Current implementation
function getColumns(fieldParameters)
{
return $http.get("api/fields", { params: fieldParameters });
}
for(var i = 0; i < $scope.model.Fields.length; i++)
{
var current = $scope.model.Fields[i];
(function(current){
fieldParameters.uid = $scope.model.Uid;
fieldParameters.type = "Columns";
fieldParameters.tableId = current.Value.Uid;
var promise = getColumns(fieldParameters);
promise.then(function(response){
current.Value.Columns = response.data;
}, error);
})(current);
}
//at this point current.Value.Columns should be filled with the response. However
//it's still empty
What can I do to achieve this?
Thanks
If I understand your question correctly, you have a list of fields that you need to do some work on. Then when all of that async work is done, you want to continue. So using the $q.all() should do the trick. It will resolve when all of the list of promises handed to it resolve. So it's essentially like "wait until all of this stuff finishes, then do this"
You could try something like this:
var promises = [];
for(var i=0; i< $scope.model.Fields.length; i++) {
var current = $scope.model.Fields[i];
promises.push(getColumns(fieldParameters).then(function(response) {
current.Value.Columns = response.data;
}));
}
return $q.all(promises).then(function() {
// This is when all of your promises are completed.
// So check your $scope.model.Fields here.
});
EDIT:
Try this since you are not seeing the right item updated. Update your getColumns method to accept the field, the send the field in the getColumns call:
function getColumns(fieldParameters, field)
{
return $http.get("api/fields", { params: fieldParameters}).then(function(response) {
field.Value.Columns = response.data;
});
}
...
promises.push(getColumns(fieldParameters, $scope.model.Fields[i])...
var promises = [];
for(var i = 0; i < $scope.model.Fields.length; i++)
{
var current = $scope.model.Fields[i];
promises.push(function(current){
//blahblah
return promise
});
}
$q.all(promises).then(function(){
/// everything has finished all variables updated
});

Using data from one async API call to another

I have a question in regards to asynchronous data calls in AngularJS.
The issue is that I will be recieving multiple objects in an array from one API call, and I will have to extend those objects with data from a different API call.
I was thinking of having nested async calls, but my logic falls a bit short in terms of how this would work with the $q service. I have a service which will return the object which has been extended with the second call so I can use this in a controller for display in a view.
The first API call returns some parameters which I need for the second API call in order to get the relevant data for which I will return back to the controller.
I will have to loop inside the first call so I can then run the second API call inside of that, but how am I going to return this back to my controller? I cannot resolve when the first loop has been run, because well, it explains itself.
What is the go-to solution for something like this?
Edit, my issue in pseudo-javascript:
returnListOfStuff().then(function (data) {
var result = data.Result;
for (var i = 0; i < result.length; i++) {
var dataForListItem = null;
returnDataForListItem(result[i].ID).then(function (data) {
dataForListItem = data;
});
for (prop in dataForListItem[0]) {
result[i].prop = dataForListItem[0][prop];
}
}
return result;
});
As is apparent, this won't work, considering it will only fetch the results from the first call returnListOfStuff(), because what is happening inside the for loop is not yet resolved. I can't really figure out how to do this with $q.all(), because I don't have the parameters from the returnListOfStuff function yet
I can't really figure out how to do this with $q.all(), because I don't have the parameters from the returnListOfStuff function yet
You can just do it in the then callback where you have them.
You can return the promise that $q.all yields from the callback to get a promise for the propped up result.
returnListOfStuff().then(function (data) {
var result = data.Result;
return $q.all(result.map(function(resultItem) {
return returnDataForListItem(resultItem.ID).then(function (data) {
for (prop in data[0]) {
resultItem[prop] = data[0][prop];
}
return resultItem;
});
}));
});
Try use $q.all() for this:
var loadQ = [];
var dataForListItem = null;
for (var i = 0; i < result.length; i++) {
loadQ.push(returnDataForListItem(result[i].ID));
}
$q.all(loadQ).then(function(values){
dataForListItem = values;//or values[0], I dnt known Your data structure;
});
If You will have problem with i value, try use:
for (var i = 0; i < result.length; i++) {
(function(e) {
loadQ.push(returnDataForListItem(result[e].ID));
})(i);
}

jQuery JavaScript Nested Asynchronous Functions callback

I'm a little confused how to determine when async function called multiple times from another one is finished a call from the last iteration:
function MainAsyncFunction(callback) {
for (var i = 0; i < 10; i++) {
SubAsyncFunction(function(success) {
if (i >= 10 && success) { // THIS IS WRONG?!
callback(true); // happens too early
}
});
}
};
function SubAsyncFunction(callback) {
SubSubAsyncFunction(function() {
callback(true);
});
}
What I'm doing is calling the Google Distance Matrix service, which has a limitation of 25 destinations, hence I'm having to split my array of destinations to call this service multiple times but I don't understand when it's finished.
and in the main bit of code I can tell that the second iteration of the loop in the MainAsyncFunction hasn't yet completed when it does a call back.
I think my problem is I haven't got my head around the order of events when dealing with Async functions in JavaScript... please explain how the subject is normally achieved.
You could use the jQuery Deferred object, which acts as a token representing the status of an async operation.
The following is a simplified example:
//set up your sub method so that it returns a Deferred object
function doSomethingAsync() {
var token = $.Deferred();
myAsyncMethodThatTakesACallback(function() {
//resolve the token once the async operation is complete
token.resolve();
});
return token.promise();
};
//then keep a record of the tokens from the main function
function doSomethingAfterAllSubTasks() {
var tokens = [];
for (var i=0; i < 100; i++) {
//store all the returned tokens
tokens.push(doSomethingAsync());
}
$.when.apply($,tokens)
.then(function() {
//once ALL the sub operations are completed, this callback will be invoked
alert("all async calls completed");
});
};
The following is an updated version of the OP's updated code:
function MainAsyncFunction(callback) {
var subFunctionTokens = [];
for (var i = 0; i < 10; i++) {
subFunctionTokens.push(SubAsyncFunction());
}
$.when.apply($,subFunctionTokens)
.then(function() {
callback(true);
});
};
function SubAsyncFunction() {
var token = $.Deferred();
SubSubAsyncFunction(function() {
token.resolve();
});
return token.promise();
};​
Perhaps the ajaxStop() event? This is a jQuery event that only fires when all active AJAX requests are completed.
The problem is that the value of i is constantly changing in the loop, finally being out of bounds after failing the loop conditional.
The easiest way to fix this is:
for( i=0; i<5; i++) { // or whatever your loop is
(function(i) {
// the value of i is now "anchored" in this block.
})(i);
}

Categories