Array is initialized
var Collect = [];
So I have a promise arguments so that I'll have a asynchronous in execution since retrieving data from firebase which then push in the array Collect takes a bit of time. Here's my code:
function loadTables(){
var promise = getDataFirebase();
promise.then(function(){
console.log("firsst");
return ProcessOfData();
}).then(function(){
console.log(Collect); //when printed, it shows the elements collected from firebase so the array is not 0.
console.log(Collect.length); // but when printeed here. it gives me 0. why?
return EndProcessLog();
}).then(function(){
});
}
Codes when retrieving data from firebase:
function getDataFirebase(){
return new Promise (function(resolve,reject){
refReview.on("value", function(snap){
var data = snap.val();
for(var key in data){ //data retrieved must be REVIEWEE NAME, REWIEVER NAME, RATING, ,CONTENT
Collect.push({
"RevieweeName": data[key].revieweeID.firstname.concat(" ",data[key].revieweeID.lastname),
"ReviewerName": data[key].reviewerID.firstname.concat(" ",data[key].reviewerID.lastname),
rating:data[key].rating,
content: data[key].content
})
}//end of for loop
}); //end of snap
resolve();
});
}
Why does it not work? Because you are resolving the promise before the asynchronous method runs. The reason why the object shows the value is the console lazy loading the object.
What do you do? Move the resolve line after the for loop inside the callback.
refReview.on("value", function(snap) {
var data = snap.val();
for (var key in data) { //data retrieved must be REVIEWEE NAME, REWIEVER NAME, RATING, ,CONTENT
Collect.push({
"RevieweeName": data[key].revieweeID.firstname.concat(" ", data[key].revieweeID.lastname),
"ReviewerName": data[key].reviewerID.firstname.concat(" ", data[key].reviewerID.lastname),
rating: data[key].rating,
content: data[key].content
})
} //end of for loop
resolve(); < --RIGHT
}); //end of snap
// resolve(); <-- WRONG
Ideally with a promise you do not use global variables, you pass the value through the resolve.
var myPromise = new Promise(function(resolve, reject) {
var str = "Hello!";
resolve(str);
});
myPromise.then(function(value) {
console.log(value);
});
Related
function issuetype(agent) {
//let i = 0;
console.log('inside issuetype');
return admin.database().ref('Support/issuetype').once('value', function(snapshot) {
var data = snapshot.val();
var array = Object.values(data);
console.log('Issues are');
console.log(array);
agent.add(`Select your issue `); //works fine
for(const val of array){
agent.add(new Suggestion(`${val}`));
}
console.log(data);
});
}
function subtype(agent) {
let data;
let value;
let id;
console.log('inside subtype');
let harry = new Promise(function(resolve,reject){
admin.database().ref('Support/issuetype').once('value', function(snapshot) {
value = agent.parameters.sub;
console.log('inside promise');
data = snapshot.val();
console.log('Key'+Object.keys(data));
console.log('Value'+value);
id = Object.keys(data).find(key => data[key] === value);
console.log('Matched id');
console.log(id);
if(id){
resolve(id);strong text
}else{
reject('not resolved');
}
});
});
harry.then(function(res){
console.log('Type of id:'+typeof(res));
console.log('id is:'+res);
agent.add(`Select your sub issue ret`);
admin.database().ref('Support/issuesubtype/'+res).once('value', function(snap) {
var snapdata = snap.val();
var values = Object.values(snapdata);
console.log(typeof(values));
console.log('SubIssues are'); // displayed in console
console.log(values);
agent.add(`Select your sub issue `); // not displayed
return agent.add(`Select your sub issue `); // not displayed
for(const k of values){
agent.add(new Suggestion(`${k}`)); // not displayed
}
});
}).catch(function(rej){
console.log(rej);**strong text**
}).then(function(rej){
console.log('Irrespctive');
});
}
intentMap.set('issuetype', issuetype);
intentMap.set('subtype', subtype);
Function subtype is called by intentMap,
And inside it harry function returns a promise, once promise is resolved I am getting data from firebase and want to display it using agent.add
Getting expected output in console.log but agent.add is blank
Whereas agent.add is working in issuetype function
Part of the problem is that you're mixing Promises and callbacks, sometimes returning the Promise, and sometimes not. It is easiest if you keep a few guidelines in mind:
Make sure you return the Promise.
Make sure your call to agent.add() is inside the .then() clause.
If you're using callback functions, those can be switched to using Promises instead in most cases.
Keep in mind that you don't need to wrap this into a new Promise, since the Firebase calls you're making return a Promise if you don't give it a callback function.
For example, your line
admin.database().ref('Support/issuesubtype/'+res).once('value', function(snap) {
should probably better be rewritten as
return admin.database().ref('Support/issuesubtype/'+res).once('value')
.then( snap => {
The important points here are that you're returning the Promise and you're using a Promise instead of a callback to handle the function.
I need to retrieve data from multiple API calls and aggregate them with the click of a button in the UI. I need to print the data once they all have been executed fully. I am returning a Promise from the function that runs a for-loop to make all API calls in succession. I am also processing the API call results as I am receiving them. Hence I resolve the promise just outside that for-loop. (Reminder, the for-loop makes some API calls inside.) Now, when I call this function, the promise gets resolved immediately and the success function runs which basically gives me empty aggregate data which is not expected. Where/how should I resolve the promise in such case?
Basic structure of my code:
forLoopFunction(xyz)
.then(function(){ // print aggregate data when successfull})
.catch(function(){ // print couldn't retrieve data })
forLoopFunction(){
return new Promise(resolve, reject){
for(var i=0; i<...){
api1_Call().$promise
.then(
api2_call().$promise
.then(
//work on aggregate data
)
.catch(reject(error))
).
.catch(reject(error))
}
//end of for loop
resolve(aggregated_data);
}
}
Edited code structure:
//$scope.requests is populated before this function call, have seen it printed
$scope.myFunc()
.then(function(result){console.log(result)})
.catch(function(error){console.log("failed")})
$scope.myFunc = function() {
var promiseArray = [];
for(var i=0; i<$scope.requests.data.length; i++) {
promiseArray.push(new Promise(function(resolve, reject) {
var requests= $scope.requests.data[i];
WorkflowsService.get({id: requests.key}).$promise
.then(
function (data) {
StepsService.query({workflowId: workflowData.id}).$promise
.then(
function (steps) {
//some variables calculation
var metadata = []; //this belongs to a single work request
//some more values pushed to metadata array
//switch case to calculate appropriate endpoint and system id
//$.ajaxSetup({async: false});
$.ajax({
url: apiEndpoint + systemId,
type: 'GET',
success: function(resp){
compartmentId = resp.compartmentId;
$.get("/api/compartments/" + compartmentId, function (resp) {
//some values pushed to metadata
});
},
error: function(resp) {
//put dummy data to metadata array
}
});
//construct a URL to be pushed into metadata
$scope.metadataString += metadata.join("\n");
Promise.resolve("resolved promise");
})
.catch( function(error){ Promise.reject("rejected"); console.log(error) } )
})
.catch( function(error){ Promise.reject("rejected"); console.log(error) } )
});
promiseArray.push(promiseObject);
}
return Promise.all(promiseArray).then(function() { return $scope.metadataString; });
}
You should maintain promise for all api call then resolve all in one time which I am doing by using promise.all, this will give you data in then callback in the form of array of each api call.
forLoopFunction(xyz)
.then(function(data){ console.log(data); // print aggregate data when successfull})
.catch(function(){ // print couldn't retrieve data })
forLoopFunction(){
var promiseArray = [];
for(var i=0; i<...){
var promiseObject = new Promise(function(resolve, reject){
api1_Call().$promise
.then(
api2_call().$promise
.then(
resolve(aggregated_data);
)
.catch(reject(error))
).
.catch(reject(error))
});
promiseArray.push(promiseObject);
}
return Promise.all(promiseArray)
}
I have an AngularJS application, which I use promises to get data from firebase database.
Here is my home-controller:
$scope.wallets;
walletDAO.getWalletsByUserId(auth.uid)
.then(function(wallets){
$scope.wallets = wallets;
$scope.$apply();
})
.catch(function(error){
console.log(error);
});
These are my two methods inside an service I call walletDAO:
this.getWalletsByUserId = function(id) {
return new Promise(function(resolve, reject) {
//codigo aqui
var dbRef = database.ref("users/" + auth.currentUser.uid);
dbRef.on('value', function(data) {
//console.log("Wallet IDs Retrieved!");
var userOnDB = data.val();
var walletIds = userOnDB.wallets;
var wallets = [];
for (i = 0; i < walletIds.length; i++) {
var x = getWalletById(walletIds[i])
.then(function(wallet){
wallets.push(wallet);
})
.catch(function(error){
console.log(error);
});
}
resolve(wallets);
}, function(error) {
reject(error);
});
});
};
var getWalletById = function(id) {
return new Promise(function(resolve, reject) {
var dbRef = database.ref("wallets/" + id);
dbRef.on('value', function(data) {
//console.log("Wallet Retrieved!");
var wallet = data.val();
//console.log(wallet);
resolve(wallet);
}, function(error) {
reject(error);
});
});
};
The second method, getWalletById, receive an wallet ID and return an wallet object from the firebase database. this mehod is called on the first method, getWalletsByUserId inside a for loop, which should wait for the second method to return the wallet before iterate to the next, so it can push it into the array. The problem is that it dont wait and the code execute the .then() method on the home-controller before the resolving of the getWalletById, leaving the $scope.wallets empty.
Any advice?
Use $q.all() to wait for all sub-promises to complete
$q.all(walletIds.map(function(id){
return getWalletById(id);
})).then(function(wallets){
...
})
Instead of manufacturing an ES6 promise from the ref.on method, use the ref.once method and bring it into the AngularJS execution context with $q.when:
function getWalletById(id) {
var dbRef = database.ref("wallets/" + id);
var es6Promise = dbRef.once('value');
return $q.when(es6Promise);
}
Only operations which are applied in the AngularJS execution context will benefit from AngularJS data-binding, exception handling, property watching, etc.
Use $q.all and promise chaining in the parent function:
this.getWalletsByUserId = function(id) {
var dbRef = database.ref("users/" + auth.currentUser.uid);
var es6Promise = dbRef.once('value')
.then(function(snapshot)
//console.log("Wallet IDs Retrieved!");
var userOnDB = snapshot.val();
var walletIds = userOnDB.wallets;
var promiseList = walletIds.map(function(id){
return getWalletById(id);
});
return $q.all(promiseList);
});
return $q.when(es6Promise);
};
The .then method returns a new promise which is resolved or rejected via the return value of the successCallback, errorCallback (unless that value is a promise, in which case it is resolved with the value which is resolved in that promise using promise chaining).
I am trying to make a Firebase database call inside of a loop and an outer firebase call. The inner Firebase database call is using data returned from the outer firebase call and the loop, which is why it is run within the outer one. The results then should be set into the state.
Problem
The value that is being retrieved in the inner Firebase database call is not being set in the state.
Theory
Since Firebase database calls are asynchronous, my guess is that the inner Firebase database call does not finish before the loop completes and sets the state.
Therefore, I created a promise for the inner Firebase database call, so that the loop would wait for the call to finish before moving onto the next item.
However, the value retrieved is still not being set.
Does anyone know why the loop does not wait for promise containing call to Firebase database?
MY ATTEMPT
userRef.on("value", function(snapshot) {
var snap = [];
// loop through each branch received from firebase
snapshot.forEach(function(data) {
var firstThingsFirst = data.val().firstThingsFirst;
var someID = data.val().someID;
var myPromise = new Promise(function(resolve, reject) {
userRef.child('somechild').child(someID).once('value').then(function(newSnapshot) {
console.log("newSnapshot = (below)");
console.log(newSnapshot.val());
resolve(newSnapshot.val());
}, function(error) {
// Something went wrong.
console.error("error (below)");
console.error(error);
reject("noValueFound")
});
});
var someValue = "";
myPromise.then(function(valueRetrieved) {
console.log(".then of promise is running...");
console.log("valueRetrieved = (below)");
console.log(valueRetrieved);
someValue = this.checkUndefined(valueRetrieved);
}.bind(this));
var array = {"firstThingsFirst": firstThingsFirst, "someValue": someValue};
snap.push(array);
});
this.setState({
snapshots: snap
});
}.bind(this));
Alternative Attempt:
userRef.on("value", function(snapshot) {
var snap = [];
// loop through each branch received from firebase
snapshot.forEach(function(data) {
var firstThingsFirst = data.val().firstThingsFirst;
var someID = data.val().someID;
var someValue = this.fetchValueByID(someID);
var array = {"firstThingsFirst": firstThingsFirst, "someValue": someValue};
snap.push(array);
});
this.setState({
snapshots: snap
});
}.bind(this));
fetchValueByID(someID) {
userProfileRef.child('someChild').child(someID).once('value').then(function(snapshot) {
console.log("snapshot (fetchValueByID) = (below)");
console.log(snapshot.val());
return snapshot.val();
})
}
I have also tried the approaches recommended by Firebase:
https://firebase.googleblog.com/2016/01/keeping-our-promises-and-callbacks_76.html
Thanks in advance.
Does anyone know why the loop does not wait for promise containing call to Firebase database?
The reason for this is that you require setState to be called after all the fetches are done. But, your code doesn't do anything to wait. You just carry on with the loop and once it's done, call setState. You never really know whether your fetches completed or not. You need a way to wait for all the the fetches. In a nutshell, there is a problem because of mixing of synchronous and asynchronous code.
You can try this. The idea is to map all the fetchValueByID (I've added a return in the beginning) calls into an array of promises and then wait for all of them to resolve (using Promise.all) before doing setState
userRef.on("value", function(snapshot) {
// loop through each branch received from firebase
// AND map to array of promises
var promises = [];
snapshot.forEach(function(data) {
var firstThingsFirst = data.val().firstThingsFirst;
var someID = data.val().someID;
promises.push(this.fetchValueByID(someID).then(function(someValue) {
return {
"firstThingsFirst": firstThingsFirst,
"someValue": someValue
};
}));
});
// Wait for all promises to resolve
Promise.all(promises).then(function(results) {
this.setState({
snapshots: results
});
}.bind(this))
}.bind(this));
fetchValueByID(someID) {
// Notice the return here
return userProfileRef.child('someChild').child(someID).once('value').then(function(snapshot) {
console.log("snapshot (fetchValueByID) = (below)");
console.log(snapshot.val());
return snapshot.val();
})
}
I've faked all the possible data and converted my solution to a simple-to-understand snippet below
var promises = [];
// Faking the snapshot
[{
a: 1,
b: 10
}, {
a: 2,
b: 20
}].forEach(function(data) {
var firstThingsFirst = data.a
var someID = data.b
promises.push(fetchValueByID(someID).then(function(someValue) {
return {
"firstThingsFirst": firstThingsFirst,
"someValue": someValue
};
}));
});
// Wait for all promises to resolve
Promise.all(promises).then(function(results) {
console.log(results);
});
function fetchValueByID(someID) {
// Dummy Promise resolution
// Notice the return here
return new Promise(function(resolve, reject) {
setTimeout(function() {
// Dummy manipulation
resolve(someID * 100);
});
}).then(function(snapshot) {
console.log("snapshot (fetchValueByID) = (below)");
console.log(snapshot);
return snapshot;
})
}
I have an array of promises, I use $q.all to execute all the promises then I do an action, somehow this action executed before the promises give answer back.
note: flightAPIService.getNearestAirports() return $http angular :
this.getNearestAirports = function (dataObj) {
console.log('Client::flight service:: get the nearest '+ dataObj.maxAirports+' airport for lat: '+dataObj.lat+'lng:'+dataObj.lng);
return $http.post('/getNearestAirports', dataObj);
//// the result return in JSON format (Server converted the response from XML to JSON)
//answer looks like:
//{"airportResponse":{"$":{"authorisedAPI":"true","processingDurationMillis":"119","success":"true"},"airports":[{"airports":[{"$":{"city":"Tel-aviv","country":"Israel","lat":"32.011389","lng":"34.886667","name":"Ben Gurion","timezone":"Asia/Jerusalem"},"code":["TLV"]}]}]}}
};
when the $q.all executes the promises (airportPromises) I expected that printing the table object will come after the promises are ready, but actually the table printed before the promise have answer:
$q.all(airportPromises).finally(function(res){
//return callback(null, table);
console.log(table);
},function(err){
callback(err);
return console.error('one promise error',err);
})
Here all the code:
this.addAirportsToTable = function (table,callback) {
var airportPromises = [];
// var defered = $q.defer();
this.whenFlightNeeded(table).then(function (result) {
var table = result;
for (let dayIndex = 0; dayIndex < table.length; dayIndex++) {
if (table[dayIndex].flight.flight) {
var origin = {
maxAirports: 3,
lat: table[dayIndex]['cityGoogleInf'][0].latitude,
lng: table[dayIndex]['cityGoogleInf'][0].longitude
};
var dist = {
maxAirports: 3,
lat: table[dayIndex + 1]['cityGoogleInf'][0].latitude,
lng: table[dayIndex + 1]['cityGoogleInf'][0].longitude
};
var promise1 = flightAPIService.getNearestAirports(origin).then(function (resultOriginAirport) {
table[dayIndex]['flight'].airport.push(resultOriginAirport.data);
});
var promise2 = flightAPIService.getNearestAirports(dist).then(function (resultDistAirport) {
table[dayIndex + 1]['flight'].airport.push(resultDistAirport.data);
});
airportPromises.concat([promise1,promise2]);
}
}
$q.all(airportPromises).finally(function(res){
//return callback(null, table);
console.log(table);
},function(err){
callback(err);
return console.error('one promise error',err);
})
});
//return defered.promise;
}
Any idea how to make sure that all promises are done then to print the table?
in this screenshot we can see that the object table was printed then the debugger goes back again to finish the promise task:
I believe the issue is your concat.
concat does not modify the existing array but returns a new one. So your calling $q.all passing it an empty array therefore is resolves instantly.
From the MDN:
Note: Concatenating array(s)/value(s) will leave the originals untouched. Furthermore, any operation on the new array will have no effect on the original arrays, and vice versa.
Quick example: https://jsfiddle.net/bunc6zg9/
On a side note $q.all resolves with an array of the values of the promises its waiting on. So if you returned your table values in the .then's from your two promises you can build your table in the $.all's then. That way you don't have a variable global to your promise chain containing its state.