Reputation: 111
I've tried await/async approaches for retrieving the collection associated with my firebase doc. The data is retrieved and debug log printed to console; however, it's after the calling function has returned. Any help would be appreciated. I've used the Angular firestore documentation: https://github.com/angular/angularfire2/blob/master/docs/firestore/collections.md
I'm using a subscription to the data and have tried await on the snapshot
// Client call
async doSearch() {
const col = await this.service.getLogbookEntries(this.searchDoc.id)
.then(entries => {
for (let i = 0; i < entries.length; i++) {
// Do something with entry
const entry = entries[0];
}
});
}
// Collection retrieval
async getLogbookEntries(docId: string): Promise<LogbookEntry[]>
// Retrieve sub collection and convert
return new Promise((resolve) => {
const entries: LogbookEntry[] = [];
console.log("Retrieving logbook entries: ", docId);
const doc = this.db.doc(`${this.endpoint}/${docId}`);
doc.collection<Usage>("Usage").snapshotChanges()
.subscribe(async a => {
for (let i = 0; i < a.length; i++) {
let data = a[i].payload.doc.data();
let id = a[i].payload.doc.id;
console.log("Cleaning ID: ", id, " Data: ", data);
entries.push(this.toLogbookEntry(id, data);
}
resolve(entries);
});
});
The client call is returning, even though there is an await on the promise returned.
Update I was able to get a proper await based on the following:
async getLogbookEntries(docId: string): Promise<LogbookEntry[]> {
const entries: LogbookEntry[] = [];
console.log("Retrieving logbook entries: ", docId);
const doc = this.db.doc(`${this.endpoint}/${docId}`);
// Explicitly convert to promise.
const cleaningCol = await doc.collection<ICleaning>("cleanings").get().toPromise();
const cleaningsChanges = cleaningCol.docChanges();
for (let i = 0; i < cleaningsChanges.length; i++) {
const id = cleaningsChanges[i].doc.id;
const data = cleaningsChanges[i].doc.data();
console.log("Cleaning: ", data);
entries.push(this.toLogbookEntry(id, LogbookType.Cleaning, data as ICleaning));
}
Upvotes: 0
Views: 307
Reputation: 163
The root cause of your problem is that you are mixing the notion of subscribing to the data in your firebase doc and retrieving the data in your firebase doc (using async/await). You will need to refactor this solution use one pattern or the other (subscribe patter or async fetching pattern).
doSearchOnChanges() {
const col = await this.service.getLogbookEntries(this.searchDoc.id, doStuffAfterRetrieval)
function doStuffAfterRetrieval = entries => {
for (let i = 0; i < entries.length; i++) {
// Do something with entry
const entry = entries[0];
}
};
}
// Collection retrieval
getLogbookEntries(docId: string, doStuff: entry => void): void
// Retrieve sub collection and convert
const entries: LogbookEntry[] = [];
console.log("Retrieving logbook entries: ", docId);
const doc = this.db.doc(`${this.endpoint}/${docId}`);
doc.collection<Usage>("Usage").snapshotChanges()
.subscribe(async a => {
for (let i = 0; i < a.length; i++) {
let data = a[i].payload.doc.data();
let id = a[i].payload.doc.id;
console.log("Cleaning ID: ", id, " Data: ", data);
entries.push(this.toLogbookEntry(id, data);
}
doStuff(entries);
});
This is more or less how you would refactor your code into a proper use of subscription. I'm not very familiar with the firebase api, but on a cursory google search to get data and not subscribe to it you need to use the .get()
function on collections. (Also ProTip: Don't ever use .then when you are using async/await. Its redundant and will lead to bugs more often then not. Pick one pattern and stick with it.)
async doSearch() {
const col = await this.service.getLogbookEntries(this.searchDoc.id);
for (let i = 0; i < col.length; i++) {
// Do something with entry
const entry = entries[0];
}
}
// Collection retrieval
async getLogbookEntries(docId: string): Promise<LogbookEntry[]> {
// Retrieve sub collection and convert
// Don't create your own promises. Async/Await and the Firbase API will do this for you
const entries: LogbookEntry[] = [];
console.log("Retrieving logbook entries: ", docId);
const doc = this.db.doc(`${this.endpoint}/${docId}`);
const returnedData = await doc.collection<Usage>("Usage").get();
for (let i = 0; i < returnedData.length; i++) {
let data = returnedData[i].payload.doc.data();
let id = returnedData[i].payload.doc.id;
console.log("Cleaning ID: ", id, " Data: ", data);
entries.push(this.toLogbookEntry(id, data));
}
return entries;
}
Upvotes: 0