angularimagehttprequestfilereaderunordered

Angular shows random image order but it should't


I try to show some images on a single page in a strict order. The images comes from a NodeJs web server as ArrayBuffer.

This is the recommended version but isn't working. The order of images is random at every page refresh:

categories = ['hero-nature', 'hero-portrait', 'hero-commercial', 'hero-panorama'];
heroesArray: any = [];
for (let category of this.categories) {
    this.fileService.getPhotos(category).subscribe((res: any) => {
        const blob = res as Blob;
        const reader = new FileReader();
        reader.onload = () => {
            this.heroesArray.push(reader.result);
        };
        reader.readAsDataURL(blob);
    });
}

Finally I figured out a working solution:

categories = ['hero-nature', 'hero-portrait', 'hero-commercial', 'hero-panorama'];
heroesArray: any = [];
const images$: Observable<ArrayBuffer>[] = [];
for (let category of this.categories) {
    images$.push(this.fileService.getPhotos(category));
}
concat(...images$).subscribe((res: any) => {
    const blob = res as Blob;
    const reader = new FileReader();
    reader.onload = () => {
        this.heroesArray.push(reader.result);
    };
    reader.readAsDataURL(blob);
});
<ul>
    <li *ngFor="let hero of heroesArray">
        <img [src]="hero" />
    </li>
</ul>

However the second approach has impact on webpage downloading speed. Can anyone explain what is going on in the first example? And what will be a better solution?


Solution

  • In your first example you're pushing only when the image is loaded but there is no order on when the each image is loaded. (A bigger image will take longer to load for example).

    A better solution would be to have a preallocated array.

    categories = ['hero-nature', 'hero-portrait', 'hero-commercial', 'hero-panorama'];
    heroesArray: any = new Array(this.categories.length);
    for (let [i,category] of this.categories.entries()) {
        this.fileService.getPhotos(category).subscribe((res: any) => {
            const blob = res as Blob;
            const reader = new FileReader();
            reader.onload = () => {
                this.heroesArray[i] = reader.result;
            };
            reader.readAsDataURL(blob);
        });
    }
    

    Here, you see that the index is determined by the initial order, not the order by which each image has been loaded.