rxjs返回地图内事件的结果

时间:2019-01-16 12:41:29

标签: typescript rxjs rxjs6

希望有人可以帮助我,有一个可观察到的对象,它在下面的http请求的映射内返回base64字符串

transform(url: string, asBase64: boolean): Observable<any> {
        return this.http
            .get(url, {responseType: 'blob'})
            .pipe(map(val => {
                if (!asBase64) {
                    return this.sanitizer.bypassSecurityTrustUrl(URL.createObjectURL(val))
                }
                let base64Data = null;
                let reader = new FileReader();
                reader.readAsDataURL(val);
                reader.onloadend = () => {
                    base64Data = reader.result; // need to return this! 
                    console.log(base64Data);

                };
            }));
    }

问题是我需要有条件地返回结果reader.onloadend = () => {

我想我需要将其转换为Promise并使用mergeMap吗?不确定如何。

更新

因此还有另外一个问题。.从mergeMap文档看来这应该可以工作,调试时我可以看到将blob传递给reader.readAsDataURL(data);,但是return reader.result;从未触发,看起来我正在丢失对reader

的引用
transform(url: string, asBase64: boolean): Observable<any> {
        return this.http
            .get(url, {responseType: 'blob'})
            .pipe(map(val =>
                <any>(asBase64 ? val : this.sanitizer.bypassSecurityTrustUrl(URL.createObjectURL(val)))
            )).pipe(mergeMap(data => {
                if (!asBase64) {
                    return of(data)
                } else {
                    let reader = new FileReader();
                    let eventObservable = fromEvent(reader, 'onloadend').pipe(map(() => {
                        return reader.result;
                    }));
                    reader.readAsDataURL(data);
                    return eventObservable;
                }
            }));
    }

2 个答案:

答案 0 :(得分:1)

问题在于FileReader onLoadEnd事件是异步的,因此它在map函数内部不起作用。

解决问题的一种方法是用Promise包装函数,用from从诺言中创建一个可观察对象,然后使用switchMap运算符将您的可观察对象映射到新的可观察对象。答应。

现在,当您预订结果的可观察对象时,它将从FileReader发出结果。如果拒绝,则可以使用subscription方法的第二个参数获取错误。

import {from} from 'rxjs'
import {switchMap} from 'rxjs/operators'

transform(url: string, asBase64: boolean): Observable<any> {  
  return this.http
    .get(url, {responseType: 'blob'})
    .pipe(
      switchMap(
        val => from( // create the observable from a promise
          new Promise((resolve, reject) => { //create a new Promise               
            if (!asBase64) {
                resolve(this.sanitizer.bypassSecurityTrustUrl(URL.createObjectURL(val))) //resolve if base64
            }            
            const reader = new FileReader();
            reader.readAsDataURL(val);

            reader.onloadend = () => resolve(reader.result); //resolve when it finishes to load the file
            reader.onerror = () => reject(reader.error); //rejects if there was an error while reading the file
          })
        )
      )
    );
}

...
const file$ = transform(url, asBase64)
file$.subscribe(
   (file) => console.log(file), // do stuff with the file here
   (error) => console.log(error)  // there was an error while reading the file
)

答案 1 :(得分:0)

Ok最终想到了这一点。因此,我需要根据响应使用mergeMap,并根据需要使用asBase64onloadend文件读取器事件中创建一个新的可观察对象。

但是由于这些行,它也不起作用(请参阅我最初问题中的代码)。

let reader = new FileReader();
                    let eventObservable = fromEvent(reader, 'onloadend').pipe(map(() => {
                        return reader.result;
                    }));
                    reader.readAsDataURL(data);
                    return eventObservable;

fromEvent方法的问题在于,该事件仅触发一次,而在我的情况下,是在我的Angular模板成功订阅可观察对象之前触发的,所以解决方案是使用Subject不确定是否理想在这种情况下,但对我来说似乎还可以。

transform(url: string, asBase64: boolean): Observable<any> {
        return this.http
            .get(url, {responseType: 'blob'})
            .pipe(map(val =>
                <any>(asBase64 ? val : this.sanitizer.bypassSecurityTrustUrl(URL.createObjectURL(val)))
            )).pipe(mergeMap(data => {
                if (!asBase64) {
                    return of(data)
                } else {
                    let reader = new FileReader();
                    let subject = new Subject<any>();
                    reader.onloadend = () => {
                        subject.next(reader.result);
                    };
                    reader.readAsDataURL(data);
                    return subject;
                }
            }));
    }