It's generally not advisable to mix promises and observables. They do play well together, but it jumbles and confuses your code. It's also very simple to convert a promise into an observable. Most operators that create/deal with higher-order observables will do the conversion for you. Otherwise, from(promise)
returns an observable as well.
Using forkJoin
In this case, forkJoin
accepts an array of Promise
without complaint.
forkJoin
will run all the promises concurrently and return an array of responses once/if they all complete/resolve.
this.links = this.currentUserRef.collection(
'links',
ref => ref.where('pendingRequest', '==', false)
).get().pipe(
mergeMap(querySnap => forkJoin(
querySnap.docs.map(
doc => doc.get('otherUser').get()
))
)
);
Using concat
concat
can also accept promises as parameters. We use the spread operator (...) to turn an array into a list of parameters.
This is actually closer to what you achieve with promises, as you await
each promise before running the next one. This will run your promises one at a time (and not concurrently, the way forkJoin
does
this.links = this.currentUserRef.collection(
'links',
ref => ref.where('pendingRequest', '==', false)
).get().pipe(
mergeMap(querySnap => concat(
...querySnap.docs.map(
doc => doc.get('otherUser').get()
))
),
toArray()
);
Aside: Cleaning up your promise code
This:
this.links = this.currentUserRef.collection('links', ref => ref.where('pendingRequest', '==', false)).get()
.pipe(map(querySnap => {
const ret = [];
querySnap.forEach(async doc => {
const val = await doc.get('otherUser').get().then(userData => {return userData});
ret.push(val);
});
return ret;
}));
is equivalent to
this.links = this.currentUserRef.collection(
'links',
ref => ref.where('pendingRequest', '==', false)
).get().pipe(
map(querySnap => {
const ret = [];
querySnap.forEach(async doc =>
ret.push(await doc.get('otherUser').get())
);
return ret;
})
)
Basically, if you're using await, you don't need .then(...
与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…