我正在使用Angular进行文件加密和上载类。这些操作很多都是异步的,因此我编写的方法返回了RxJS Observable。
// 1.
private prepareUpload(file): Observable<T>;
// 2.
private encryptData(data, filekey): Observable<T>
// 3.
private uploadEncryptedData(formData, token, range): Observable<T>
// 4.
private completeUpload(updatedFilekey, token): Observable<T>
我想将此逻辑封装在公共upload(file)
方法中,最终我使用了嵌套订阅,并且可以使用,但是我知道这是错误的,并且出于多种原因,RxJS中使用了反模式。这是代码的简化版本:
public upload(file) {
const gen = this.indexGenerator(); // generator function
this.prepareUpload(file).subscribe(values => {
const [response, filekey, data] = values;
this.encryptData(data, filekey).subscribe(encryptedDataContainer => {
const formData = this.prepareEncDataUpload(encryptedDataContainer.data, file.name)
const range = this.getRange(file.size, gen.next().value);
this.uploadEncryptedData(formData, response.token, range).subscribe(() => {
if (range.isFinalPart) {
this.completeUpload(encryptedDataContainer.updatedFilekey, response.token).subscribe(console.log);
}
});
});
});
}
我无法使用多个RxJS运算符的组合来清除此代码。我的目标是避免嵌套订阅,而是在工作流完成时从公共upload()
方法返回单个Observable。
谢谢!
答案 0 :(得分:0)
您要在订阅前使用管道。管道允许您在流发出值之前对流中的值进行更改。另外,使用mergeMap
展平订阅链。这里是概述。这不能提供完整的解决方案-付给我的钱还不够;)-但这足以为您指明正确的方向:
this.prepareUpload(file).pipe(
tap(values => console.log('hello1', values)),
map(values => [response, filekey, data]),
tap(values => console.log('hello2', values)),
mergeMap(() =>
// essential to catchError else an HTTP error response will disable this effect - if it uses HTTP - catchError essentially prevents the stream from erroring in which case it will never emit again
this.encryptData(data, filekey).pipe(
map(res => res), // do something with res here if you want
catchError(() => {
return of(null)
})
)
),
filter(res => !!res)
// more mergemap stuff here
).subscribe(values => {
console.log(values)
})
提示:使用tap运算符在流向下传递值时使用console.log值
PS:未选中语法,可能缺少逗号或方括号或2
PPS:管道中的函数都是RxJS运算符
答案 1 :(得分:0)
您可以使用mergeMap rxjs运算符合并这些可观察对象,并摆脱嵌套订阅。
尽管有一个陷阱, 请注意,由于mergeMap一次维护多个活动的内部订阅,因此可能通过长期存在的内部订阅造成内存泄漏。
作为参考和示例: https://www.learnrxjs.io/operators/transformation/mergemap.html
答案 2 :(得分:0)
我认为将您的可观察物链接起来就可以做到,您可以通过 flatMap(mergeMap的别名)可能是-https://stackoverflow.com/a/37777382/9176461和 RxJS Promise Composition (passing data)
正如我在评论中所提到的,类似以下的内容应该起作用(伪代码):
public upload(file) {
const gen = this.indexGenerator(); // generator function
return Rx.Observable.just(file).pipe(
mergeMap(this.prepareUpload),
mergeMap(this.encryptData),
mergeMap(this.prepareEncDataUpload),
mergeMap(this.prepareEncDataUpload),
.... )
}
答案 3 :(得分:0)
您可以使用RxJ中的mergeMap
和filter
运算符并链接您的呼叫。您将需要创建一些函数级变量以在链接过程中使用。
import { mergeMap, filter, catchError } from 'rxjs/operators`
public upload(file) {
const gen = this.indexGenerator(); // generator function
let range, token;
this.prepareUpload(file)
.pipe(
mergeMap((values) => {
const [response, filekey, data] = values;
token = response.token;
return this.encryptData(data, filekey);
}),
mergeMap(encryptedDataContainer => {
const formData = this.prepareEncDataUpload(encryptedDataContainer.data, file.name)
range = this.getRange(file.size, gen.next().value);
return this.uploadEncryptedData(formData, token, range);
}),
filter(() => !!range.isFinalPart),
mergeMap(() => {
return this.completeUpload(encryptedDataContainer.updatedFilekey, token);
})
catchError((error) => {
console.log(error);
// handle the error accordingly.
})
)
.subscribe(() => {
console.log('success');
});
}