Angular 2:map函数内含异步操作时如何返回?如何等待onloadend事件?
如何让RxJS Observable等待FileReader异步操作完成后返回结果
嘿,这个问题我太熟了——你遇到的核心矛盾是:FileReader的onloadend是异步事件,但RxJS的map操作符是同步处理上游Observable值的,根本没法等异步操作完成再返回结果。要解决这个,我们得把FileReader的异步流程包装成Observable,再用合适的操作符衔接两个Observable。
先给你修正后的完整可运行代码
getImage(imageId: string): Observable<ImageSource> { return this.http.get(`${ImagesUrl}/${imageId}`, { responseType: 'blob', observe: 'response' }) .pipe( // 先纠正一个小错误:observe: 'response'时,返回的是HttpResponse对象,Blob在body字段里 map(response => response.body as Blob), // 用switchMap切换到FileReader的Observable,等待异步操作完成 switchMap(blob => { return new Observable<ImageSource>(observer => { const reader = new FileReader(); // 异步操作完成时,把结果推给Observable订阅者 reader.onloadend = () => { const base64Data = reader.result as string; observer.next(base64Data as unknown as ImageSource); observer.complete(); }; // 别忘了处理错误,保证Observable的健壮性 reader.onerror = (errorEvent) => { observer.error(errorEvent.error); }; reader.readAsDataURL(blob); }); }) ); }
关键细节解释
- 为什么用
switchMap而不是map?map只能同步转换上游值,而我们需要从Http请求的Observable切换到一个新的、等待FileReader完成的Observable。switchMap会自动取消前一个未完成的请求(如果用户重复调用这个方法的话),非常适合这种场景。 - Blob的正确提取
你之前直接把response赋值给blob是错的——当设置observe: 'response'时,http请求返回的是HttpResponse<Blob>实例,真正的Blob数据存在response.body里。 - 包装FileReader为Observable
用new Observable()创建自定义流,在onloadend时通过observer.next()传递结果,完成后调用observer.complete();同时处理onerror事件,把错误推给订阅者,避免流被卡住。
更简洁的Promise包装写法
如果你更喜欢Promise的写法,也可以把FileReader包装成Promise,再用from操作符转换成Observable,代码会更清爽:
import { from } from 'rxjs'; import { map, switchMap } from 'rxjs/operators'; getImage(imageId: string): Observable<ImageSource> { return this.http.get(`${ImagesUrl}/${imageId}`, { responseType: 'blob', observe: 'response' }) .pipe( map(response => response.body as Blob), switchMap(blob => { // 把FileReader转换成Promise const readBlobPromise = new Promise<string>((resolve, reject) => { const reader = new FileReader(); reader.onloadend = () => resolve(reader.result as string); reader.onerror = (err) => reject(err); reader.readAsDataURL(blob); }); // 把Promise转换成Observable,再转换类型 return from(readBlobPromise).pipe( map(base64 => base64 as unknown as ImageSource) ); }) ); }
两种写法都能解决你的问题,选你觉得更顺手的就行~
内容的提问来源于stack exchange,提问作者Pratap A.K
相关产品推荐
相关产品推荐

