首页 > 解决方案 > rxjs返回地图内事件的结果

问题描述

希望有人可以帮助我有一个可观察的,它在下面的 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 = () => {

我想我需要将其转换为承诺并使用mergeMap?不知道怎么做。

更新

所以对此又进行了一次尝试。从mergeMap docs看起来这应该可以工作,调试我可以看到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;
                }
            }));
    }

标签: typescriptrxjsrxjs6

解决方案


问题是 FileReader onLoadEnd 事件是异步的,所以它在 map 函数中不起作用。

解决问题的一种方法是使用 Promise 包装函数,使用from从 Promise 创建一个 observable ,然后使用switchMap运算符将您的 observable 映射到 Promise 中的新 observable。

现在,当您订阅生成的 observable 时,它​​将从 FileReader 发出结果。如果它拒绝,您可以使用 subscribe 方法的第二个参数获取错误。

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
)


推荐阅读