问题
I have the following code in an Angular app:
services$
.pipe(
map(serviceModels => {
serviceModels
.forEach((srv, srvIdx) => {
// Resolve images in services
srv.images.forEach(async (img, imgIdx) => {
serviceModels[srvIdx].images[imgIdx] = await this.imageSrv.resolveImage(img).toPromise();
});
});
return serviceModels;
});
[...]
Result is a single emition with one value change afterwards.
emit -> service.image[0] // 'unrendered-url' -> (wait) -> service.image[0] // correct-url/image-path
I'm trying to resolve the image from a service (this.imageSrv) that takes a string and returns a observable/promise with a rendered string. All I want is the code to block (or map() to hold the emition until the images are resolved) because I get two values in Angular for the services[0].images[0]
.
The easy way out is to put promises in the service[0].images
array and img | async
in the template engine, but I'd like to avoid that and maybe learn something new.
回答1:
You're currently trying to run new observables inside a map
- which is for synchronously transforming the result of an observable into a different structure. I will ignore the attempt to await the promise, as that will be addressed by using the underlying observables directly.
The approach I'm going to take:
- Use
switchMap
to chain an inner observable to the outer observable - Flatten the nested image arrays
- Create an array of observables from the flattened image array
- Run the observable array in a
forkJoin
- Update the resolved images in the
serviceModels
when theforkJoin
completes - Return the
serviceModels
in amapTo
services$.pipe(
switchMap(serviceModels => {
// flatten images
const images = serviceModels.reduce((acc, srv) => acc.concat(srv.images), []);
// map to array of observables
const observables = images.map(img => this.resolveImage(img));
// run observables in parallel
return forkJoin(observables).pipe(
tap(images => {
// loop over original nested arrays
// extract image from flattened images result
let flatIdx = 0;
serviceModels.forEach(srv => {
srv.images.forEach((img, imgIdx) => {
srv.images[imgIdx] = images[flatIdx++];
});
});
}),
// return serviceModels from the outer observable
mapTo(serviceModels)
)
})
)
[...]
By the way, I would recommend not overwriting the original images with the resolved images. While this is valid Javascript, you'll run into issues with Typescript if you're using types (and I don't see any reason not to).
DEMO: https://stackblitz.com/edit/angular-a6fblk
来源:https://stackoverflow.com/questions/60590846/nested-observable-promise-trying-to-block-wait-flow-in-mapper-function