Angular图片显示顺序随机不符合预期,求原因及更优方案
图片加载顺序异常原因及优化方案
问题背景
我需要在页面中严格按顺序展示图片,图片从Node.js服务器以ArrayBuffer形式获取。最初的写法无法保证顺序,每次刷新图片顺序随机:
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); }); }
后来改用concat实现了顺序加载,但这种方式会串行请求,影响页面下载速度:
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>
现询问:第一种写法顺序随机的原因,以及更优的解决方案。
一、顺序随机的原因
- 第一种写法中,
for循环会同时发起所有HTTP请求,这些请求是异步并行执行的。 - 服务器返回响应的速度受图片大小、服务器负载、网络延迟等多种因素影响,哪个请求先完成,对应的图片就会先被
push到heroesArray中。 - 由于每次请求的响应时间不固定,最终数组的元素顺序会随机变化,无法和
categories的初始顺序保持一致。
二、更优解决方案
我们需要并行请求保证下载速度,同时按初始顺序填充结果,可以通过以下两种方式实现:
方案1:使用forkJoin + 按原顺序处理结果
forkJoin会等待所有Observable都完成后,按原始数组的顺序返回所有结果,既能并行请求提升速度,又能保证顺序:
categories = ['hero-nature', 'hero-portrait', 'hero-commercial', 'hero-panorama']; heroesArray: any = []; // 把每个请求转换为返回dataURL的Observable const imageDataURL$ = this.categories.map(category => this.fileService.getPhotos(category).pipe( switchMap(res => { return new Observable<string>(observer => { const blob = res as Blob; const reader = new FileReader(); reader.onload = () => { observer.next(reader.result as string); observer.complete(); }; reader.readAsDataURL(blob); }); }) ) ); forkJoin(imageDataURL$).subscribe(dataURLs => { this.heroesArray = dataURLs; // 直接按原顺序赋值 });
方案2:保留并行请求,按索引填充数组
如果希望图片加载完成后就逐步渲染(而非等待全部完成),可以给每个请求绑定对应的索引,按索引插入数组:
categories = ['hero-nature', 'hero-portrait', 'hero-commercial', 'hero-panorama']; heroesArray: any = new Array(this.categories.length); // 初始化固定长度数组 this.categories.forEach((category, index) => { this.fileService.getPhotos(category).subscribe((res: any) => { const blob = res as Blob; const reader = new FileReader(); reader.onload = () => { this.heroesArray[index] = reader.result; // 按索引赋值,保证顺序 }; reader.readAsDataURL(blob); }); });
这两种方案都能在并行请求的基础上保证图片顺序,同时避免了concat串行请求导致的速度问题。
内容的提问来源于stack exchange,提问作者Pelikanus Maximus
相关产品推荐
相关产品推荐

