I have a three-step process that needs to run synchronously. First, the app pulls information from one firebase collection, then using that result, pulls information from a second collection, then using that result, performs a function.
These are not running synchronously, so I'm missing the data assignments.
I believe the solution is working with promise.all, but I cannot structure it correctly:
Original Code:
getUserInfo(user)
.then(() => {
//get data from database from result of getUserInfo
getStyles(userInfo.value);
})
then(() => {
//do things with data from database from result of
getStyles
console.log(info);
});
With Promise.all:
let newUser = getUserInfo(user);
let newStyles = getStyles(userInfo.value)
Promise.all([newUser, newStyles]).then(() => {
console.log("do things")
}
The error is that the value newUser is still undefined.
It seems like you forgot to return anything from your promises so all you get is undefined.
Try like this.
getUserInfo(user)
.then((userInfo) => {
//get data from database from result of getUserInfo
return getStyles(userInfo.value);
})
then((stylesInfo) => {
//do things with data from database from result of getStyles
console.log(stylesInfo);
});
Related
I am developing a business manager web app in React with Firebase back-end. I am also coding a local API to simplify Firebase functions. I created this method, which loads data from a Firebase collection and returns an array of documents.
getDocuments(collection) {
var documents = [];
firebase.firestore().collection(collection).get().then(snapshot => {
snapshot.forEach(doc => {
documents.push(doc);
});
}).then(() => {
return documents;
})
}
However, when I call the method and assign it to a variable which I later print to the console, it says undefined.
var employees = getDocuments("employees");
console.log(employees);
What I want to do is to use .then() after calling the method to print the already loaded data to the console. Something like this:
var employees = getDocuments("employees").then(response => {
console.log(response);
})
Any help would be appreciated. Thank you.
Your getDocuments function seems to be unnecessarily complicated. This:
getDocuments(collection) {
return firebase.firestore().collection(collection).get().then(snapshot=>snapshot.docs)
}
yields exactly the same intended result (an Array of docs wrapped in a promise) as your function but performs faster since it skips looping through all the documents in the snapshot https://firebase.google.com/docs/reference/js/firebase.firestore.QuerySnapshot#docs
Afterwards just extract the value from the Promise returned by this function in your preferred way:
Option 1 (async/await)
let employees= await getDocuments('employees')
console.log(employees)
Option 2 (chaining)
let employees =[]
getDocuments('employees').then(response => {
employees=response
console.log(employees)
})
Explanation
When you are doing this:
var employees = getDocuments("employees").then(response => {
console.log(response);
})
you aren't receiving any value from getDocuments since you didn't return anything in the first place.
getDocuments(collection) {
var documents = [];
firebase.firestore().collection(collection).get().then(snapshot => {
snapshot.forEach(doc => {
documents.push(doc);
});
}).then(() => {
return documents; <-- this is returning the value of documents to the parent scope which is 'getDocuments', since the 'then' is related to the 'get' function
})
}
You should assign employees in the then like this
var employees = [];
getDocuments("employees").then(response => {
employees = response;
console.log(response);
})
Or if you are in an asynchronous function, you could go for something like this
var employees = await getDocuments("employees");
console.log(employees);
But the await keyword has to be done in an async function
I would need to get the data returned by using the Neo4j driver for Node JS. My problem is, that i can print the value of 'online' out on console inside the .then call but i can't seem to get access to it outside of that part - I have tried returning record.get('onl'), assign it to a pre-defined variable outside the function but nothing works - all i get as result if i try to, for example, print the value of online out at the last line in this snippet, is Promise { <pending> }. I suppose I don't do the promise handling right, and I looked up lots of tutorials and examples, but I can't work it out. So: how could i assign the returned data (record.get('onl')) to var online and get actual result instead of the promise?
Thanks in advance :)
var online = session.run(cyp1, param).then(results => {
return results.records.map(record =>{
console.log(record.get('onl'))
return record.get('onl')
})
}).then(()=>{
session.close()
});
console.log(online)
Currently, you are assigning var online as a 'Promise Chain' & not a 'Resolved Promise'. You could use Async/Await this will allow you to write async code in a synchronous manner.
async function getRecords(){
const records = await session.run(cyp1, param);
return records.map(record => record.get('onl'))
}
const online = await getRecords();
Use try/catch/finally
try {
const online = await getRecords();
} catch (error) {
// do something
} finally {
await session.close()
}
If you wanted to continue using .then()
You need to use 'Promise Chaining' and pass the value 'Down the chain', this results in complex 'Callback'/'Promise Chain' hell.
session.run(cyp1, param).then(results => {
return results.records.map(record => record.get('onl'))
}).then((online)=> {
console.log(online)
}).catch(() => {
// do something
}).finally(() => {
session.close()
});
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;
First of all, there are some issues with console.log in Google Chrome not functioning as expected. This is not the case as I am working in VSCode.
We begin with two async calls to the server.
promise_a = fetch(url)
promise_b = fetch(url)
Since fetch results are also promises, .json() will needed to be called on each item. The helper function process will be used, as suggested by a Stackoverflow user -- sorry lost the link.
let promiseResults = []
let process = prom => {
prom.then(data => {
promiseResults.push(data);
});
};
Promise.all is called. The resulting array is passed to .then where forEach calls process on item.json() each iteration and fulfilled promises are pushed to promiseResults.
Promise.all([promise_a, promise_b])
.then(responseArr => {
responseArr.forEach(item => {
process(item.json());
});
})
No argument is given to the final .then block because promiseResults are in the outer scope. console.log show confusing results.
.then(() => {
console.log(promiseResults); // correct results
console.log(promiseResults[0]); // undefined ?!?
})
Any help will be greatly appreciated.
If you are familiar with async/await syntax, I would suggest you not to use an external variable promiseResults, but return the results on the fly with this function:
async function getJsonResults(promisesArr) {
// Get fetch promises response
const results = await Promise.all(promisesArr);
// Get JSON from each response promise
const jsonResults = await Promise.all(results.map(r => r.json()));
return jsonResults
}
This is usage example:
promise_a = fetch(url1)
promise_b = fetch(url2)
getJsonResults([promise_a, promise_b])
.then(theResults => console.log('All results:', theResults))
Use theResults variable to extract necessary results.
You can try this, it looks the array loop is not going properly in the promise env.
Specifically: the promiseResults is filled after you are logging.
var resultAll = Promise.all([promise_a, promise_b])
.then(responseArr => {
return Promise.all(responseArr.map(item => return item.json()));
});
resultAll.then(promiseResults => {
console.log(promiseResults);
});
I have the following code where the modifying the db item base on an inner promise is not working.
$('input[type=checkbox][name=checklist]:checked').each(function()
{
var collection=db.items.where("name").equals(checkbox.val());
var success;
collection.modify(function(item){
var invokePromise;
//invokePromise = a fucntion that returns a promise
//this invokepromise function needs the item from the db.
invokePromise.then(function(thirdPartyResponse){
item.date=new Date();
item.attempts= item.attempts+1; <-- this is not being updated.
}).catch(function(error){
delete this.value; <-- this is also not deleted
});
});
});
Considering #David Fahlander answer that Collection.modify() must synchronously update the item, you should collect the async responses first and after alter the database. You can use Promise.all() to asynchronously collect the responses from the invokePromise and modify the database in one go afterwards.
A callback given to Collection.modify() must synchronously update the item. You could also optimize the query using anyOf() instead of equasls. Here's an example that examplifies another strategy:
function yourAction () {
const checkedValues = $('input[type=checkbox][name=checklist]:checked')
.toArray() // Convert to a standard array of elements
.map(checkBox => checkBox.value); // Convert to an array of checked values
return invokePromise.then(thirdPartyResponse => {
return db.items.where("name").anyOf(checkedValues).modify(item => {
item.date = new Date();
++item.attempts;
}).catch(error => {
console.error("Failed to update indexedDB");
throw error;
});
}).catch(error => {
// Handle an errors from invokePromise
// If an error occurs, delete the values. Was this your intent?
console.error("Error occurred. Now deleting values instead", error);
return db.items.where("name").anyOf(checkedValues).delete();
});
}
You could retrieve all the entries, wait for the promises and then update them seperately:
(async function() {
const entries = await db.items
.where("name").equals(checkbox.val())
.toArray();
for(const entry of entries) {
//...
await invokePromise;
await db.items.put(entry);
}
})();
You might want to parallelize the whole thing with entries.map and Promise.all and Table.putAll.