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.
Related
How does nested return in javascript work? I have tried to find answer in the good internet but find very few sites/examples and still the concept is unclear. What happens when the first return is reached and then the second return?
function getSelected(empid) {
let utils = myCompany.Xrm.Utils;
let cleanid = utils.cleanGuid(empid);
let fetchXml = [
'<fetch >',
.....
.....
.....
.....
'</fetch>',
].join('');
let com = myCompany.Xrm.Common;
// ** here:
return com.ExecuteFetch(fetchXml, 'entity', true).then(function(result) {
let selected = [];
result.forEach(function(interesting_topic) {
let id = interesting_topic.id;
selected.push(id);
});
// ** and here:
return selected;
}).catch(function(error) {
console.log(error);
return null;
});
}
These aren't "nested returns", they each return from a different function.
The first one returns the result of com.ExecuteFetch(), which is a Promise. So the getSelected() function returns a Promise. At that point, that function is done. It has returned its value.
At some later time, when the Promise is resolved, anything which observes that result will see that the resolution of that promise returns whatever is in selected. So for example, if calling code does this:
let result = await getSelected(someValue);
console.log(result);
Then result will be the returned value from selected. Or if await is not an option in that context, you can use the same .then() structure shown in the code you have:
getSelected(someValue).then(function (result) {
console.log(result);
});
In both cases, getSelected() returned a Promise, and the callback function which resolves the promise returned a value. Different functions, different returns.
When calling a function that returns a promise, comes back as undefined unless async operators are removed, then returns ZoneAwarePromise, but contains no data.
I know the query returns data when the function executes, it however does not seem to pass that data to the actual return part of the function call.
I have looked at several Stack questions that have not answered this question including this question:
Async/Await with Request-Promise returns Undefined
This is using a REST endpoint to pull data, the console.logs do show the data is correct, however return comes back as undefined
this.allPeople.forEach(async person => {
const dodString = await this.getRelatedRecords(person); //undefined
}
This is the main function that returns a promise / data
async getRelatedRecords(person) {
// function truncated for clarity
// ...
//
console.warn('This async should fire first');
selPeopleTable.relationships.forEach(relationship => {
allRelationshipQueries.push(
arcgisService.getRelatedTableData(
selPeopleTable.url, [person[oidField.name]], relationship.id, relationship.name),
);
});
await Promise.all(allRelationshipQueries).then(allResults => {
console.log('Inside the Promise');
// The Specific node I am looking for
const data = allResults[1].results.relatedRecordGroups[0].relatedRecords[0].attributes.dod;
console.log(data); // Shows correctly as the data I am looking for
return data;
}).catch(function(data){
console.log('there might be data missing', data);
});
}
Removing the ASYNC operators cause the getRelatedRecords() to fire after the containing function and / or return a 'ZoneAwarePromise' which contains no data. I need getRelatedRecords() to fire first, then to run the rest of the code.
I can provide more snippets if need be.
Zone Aware Promise
When the Async operators are (I think) setup correctly
You need to return this as well:
await Promise.all(allRelationshipQueries).then(allResults => {
console.log('Inside the Promise');
// The Specific node I am looking for
const data = allResults[1].results.relatedRecordGroups[0].relatedRecords[0].attributes.dod;
console.log(data); // Shows correctly as the data I am looking for
return data;
})
return in the above block is returning but all of this is in the scope of the arrow function which is then(allResults => { so you also need to return this function like this:
return await Promise.all(allRelationshipQueries).then(allResults => {
Approach #2:
Second way would be to store that into variable like this:
let dataToReturn = await Promise.all(allRelationshipQueries).then(allResults => {
console.log('Inside the Promise');
// The Specific node I am looking for
const data = allResults[1].results.relatedRecordGroups[0].relatedRecords[0].attributes.dod;
console.log(data); // Shows correctly as the data I am looking for
return data;
}).catch(function(data){
console.log('there might be data missing', data);
});
return dataToReturn;
I'm developing a Dapp based on Ethereum and I got stuck with Promises.
In the for loop, the elements of the array have to be verified one by one. This happens at the validateRow() function, which returns a Promise at first. The Promise will be resolved to a number (0, when the element is valid; 1, 2 or 3, when it is not valid).
In the end, I would like to return a resultList[], which is an array of objects. Each object should have two properties:
row, containing the element (a string),
and result, which tells whether it is valid.
However, the resultList[] only contains the rows in the end, while the 'then' branch only holds the results ({"row":"","result":"0"}). I added the logs which are printed in the console as comments. Unfortunately, I can't figure out, how I could put the two together.
var resultList = [];
for (var i = 0; i < App.resultArray.length; i++) {
var promiseReturned = contractInstance.validateRow.call(App.resultId, App.resultArray[i]);
console.log(promiseReturned); //Promise {<pending>}
var rowObject = new Object();
console.log(App.resultArray[i]); //row1string
rowObject.row = App.resultArray[i];
promiseReturned.then(function(returnVal) {
console.log("the returnVal: " + returnVal); //the returnVal: 1
rowObject.result = returnVal;
console.log("the rowObject :" + JSON.stringify(rowObject)); //{"row":"","result":"0"}
return returnVal;
});
resultList.push(rowObject);
};
console.log(resultList); //[{"row":"row1string"},{"row": "row2string"}]
return resultList;
In Javascript, use forward slashes to denote comments, not backslashes, else you'll get syntax errors.
Use Promise.all to wait for all promises to be resolved before returning the object:
async function getResultList() {
const allPromises = App.resultArray.map((row) => (
contractInstance.validateRow.call(App.resultId, row)
.then(result => ({ result, row }))
));
const resultList = await Promise.all(allPromises);
return resultList; // This will return a Promise to the caller of getResultList
}
Note that you'll have to consume getResultList as a promise, since it doesn't run synchronously. eg
const resultList = await getResultList();
For completeness, CertainPerformance's answer but using async/await, and rewritten more concisely:
async function getResultList() {
return await Promise.all(
App.resultArray.map(async (row) => {
const result = await contractInstance.validateRow.call(App.resultId, row);
return {
row,
result,
};
})
);
}
I am trying to get the promise returned from another function. Here is my code:
function getData(){
var result = [];
return new Promise(function(resolve){
var query = firebase.database().ref('groups');
query.once('value').then(data => {
data.forEach(snapshot => {
var gdata = snapshot.val();
var grp = snapshot.key;
result.push(grp);
});
});
resolve(result);
});
}
I managed to print out the data for 'grp' variable from the function above. In my second fuction where I tried to access the data returned from promise:
However, from the second function, when I tried to print out in the .then(), nothing is actually printed out. Why is it so?
Thanks!
First, firebase already returns a Promise, so there's no need to create a new one. You should put the data into result and return the promise directly.
function getData() {
var query = firebase.database().ref('groups');
return query.once('value').then(data => {
let res = [];
data.forEach(snapshot => {
var gdata = snapshot.val();
var grp = snapshot.key;
res.push(grp);
});
return res;
});
}
You can then simply chain that
function plotData() {
return getData().then(data => {
data.forEach(console.log);
});
}
The bad news: If you need to use a .then() function to get the value you want (as with your firebase method) then you have to return a promise from your function. This is the case even for your inner function.
The good news is that .then() can be used to "convert" the return value of a function; and will even unravel returned promises for you (if, say, you decide you need to perform a second database call before you have your answer)
function getData(){
var query = firebase.database().ref('groups');
return query.once('value').then(data => {
var result = [];
data.forEach(snapshot => {
var gdata = snapshot.val();
var grp = snapshot.key;
result.push(grp);
});
return result;
});
resolve(result);
}
Take note how I return result inside of the .then() function. Since I'm returning the result of .then(), this will then evaluate to a promise that, rather than purely returning data, will return result.
I'm less certain what you're doing with the second block. You shouldn't need the Promise.all or map around getData unless you actually intend to call it multiple times; and given that your list is empty, it seems like it's not called at all.
DISCLAIMER: I know the promises concept well, but not firebase. If firebase has unique aspects to it that change how this should work, I may be wrong.
You resolve the promise too early. But don't create new promises if you don't need to:
function getData(){
var result = [];
var query = firebase.database().ref('groups');
return query.once('value').then(data => {
return data.map(snapshot => snapshot.key);
});
}
list.map() does not resolve, it is not a promise... Promise.all needs to receive a promise.
You probably wanted to invoke getData() there.
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;
})
}