Angular 使用rxjs链接订阅一个观察值数组

Angular 使用rxjs链接订阅一个观察值数组,angular,rxjs,Angular,Rxjs,我想在Angular 5中一次上传一组文件(或者至少尝试上传,个人失败是可以的),然后知道所有订阅何时完成 在Javascript的旧时代,我会使用链式回调来实现这一点,但现在我们有了像rxjs这样的优秀工具,我感觉有一种更好、更“反应式”的方法来实现这一点 那么:用rxJS实现这一点的最佳方法是什么? 我有一个Angular provider,它上传一个文件并返回一个可观察的数据;我想尝试分别上传阵列中的每个文件,并知道它们何时全部完成 在下面的示例中,我用一个简单的主题替换了提供者,该主题在

我想在Angular 5中一次上传一组文件(或者至少尝试上传,个人失败是可以的),然后知道所有订阅何时完成

在Javascript的旧时代,我会使用链式回调来实现这一点,但现在我们有了像
rxjs
这样的优秀工具,我感觉有一种更好、更“反应式”的方法来实现这一点

那么:用rxJS实现这一点的最佳方法是什么?

我有一个Angular provider,它上传一个文件并返回一个可观察的数据;我想尝试分别上传阵列中的每个文件,并知道它们何时全部完成

在下面的示例中,我用一个简单的主题替换了提供者,该主题在一段随机的时间后以随机的成功或错误完成,试图模拟不稳定的Internet连接

问题:当我使用Observable.CombineTest()时,我只会在所有Observable都下一个()生成结果时得到最终结果:
[0,1,2,3,4,5,6,7,8,9]
。如果不是所有的观测都完成了,我永远也得不到结果

此外,观测值不是一次运行一个,而是一次运行全部。当与AJAX请求一起使用时,它可能会使单元格连接过载

你对如何处理这个问题有什么想法吗

constructor() {
    let observables = [];

    for (var i = 0; i < 100; i++) {
        observables.push(this.testObservable(i));
    }


    Observable.combineLatest(observables)
        .subscribe(res => {
            console.log('success', res);
        }, err => {
            console.log('errors', err);
        })
}



testObservable(param) {
    let subject = new Subject;

    let num = Math.random() * 10000;
    console.log('starting', param)
    setTimeout(() => {

        if (Math.random() > 0.5) {
            console.log('success', param);
            subject.next(param);
        } else {
            console.log('error', param);
            subject.error(param);
        }

        subject.complete();
    }, num);

    return subject;
}
constructor(){
设可观测项=[];
对于(变量i=0;i<100;i++){
observables.push(this.testObservables(i));
}
可观测。组合相关(可观测)
.订阅(res=>{
console.log('success',res);
},err=>{
console.log('errors',err);
})
}
testObservable(参数){
让主题=新主题;
设num=Math.random()*10000;
console.log('starting',param)
设置超时(()=>{
if(Math.random()>0.5){
console.log('success',param);
主题。下一个(param);
}否则{
console.log('error',param);
主语错误(param);
}
subject.complete();
},num);
返回主题;
}

您可以使用
CombineTest
等待一组可观测数据完成。它将发射一次,每次提供的可观测数据至少发射一次

let files = [uri1, uri2, uri3];
const observables = files.map(file => this.addMedia({ uri: file, post_id: res.post.Post.id }));

Observable.combineLatest(observables).subscribe(() => console.log('All complete'));
如果您希望观察对象一个接一个地执行,您可以使用
concat
(对于有序)或
merge
(如果顺序不重要)

为了捕获错误,您可以向每个可观察对象添加一个
catch
操作符,并返回一个空的可观察对象或更合适的对象

this.addMedia({ uri: file, post_id: res.post.Post.id })
    .do(val => console.log(`emitting: ${val}`))
    .catch(err => {
      console.log(`error: ${err}`);
      return Observable.empty();
    });

子可观察对象应该发出一个
next()
,或者抛出一个错误。在这两种情况下,可观察的子对象都应完成。如果子observable没有完成,那么您的子observable中有一个bug。如果您知道observable子对象有一个bug并且永远不会完成,那么您可以使用超时,但您真的不应该这样做

由于子观测值中的错误是好的,所以您应该捕捉它们。最后,如果您想缓冲这些项目,使它们不会一次发送到服务器,那么您也可以这样做

把它们放在一起():

const start=Date.now();
函数(){
返回日期.now()-开始;
}
可观测的功能(延迟、错误){
//失败的可观察事物
如果(错误){
返回Rx.可观察到的抛出(错误);
}
//经过一段时间后成功的可观察事物
如果(延迟){
返回Rx.observate.create(observator=>{
log(经过的()+':向服务器发出的请求');
setTimeout(()=>observer.next(),延迟);
});
}
//一个永远不会完成的可观察对象(你真的不应该拥有这些)
返回Rx.Observable.create(()=>{});
}
函数formatResult(结果){
if(result.failed){
返回'FAIL('+result.error+');
}否则{
返回“通行证”;
}
}
const obs1=可观测(1000);
const obs2=可观测(500);
const fails=dummyObservable(null,新错误(‘此错误’);
const neverFinishes=dummyObservable();
const observes=[obs1,obs2,failes,neverfinishs];
//我们只想要第一个回应。仅当源观测值在发出一项后未完成时才需要
const firstOnly=observables.map(obs=>obs.first());
//仅允许5秒钟,如果5秒钟后没有响应,则中止
const timeoutsHandled=firstOnly.map(obs=>obs.timeout(5000));
//如果发生任何故障,则处理它们
const failureshandle=timeoutsHandled.map(obs=>obs.map(()=>({failed:false})).catch((err)=>Rx.Observable.of({failed:true,error:err}));
缓冲常数=[];
//缓冲请求,以便在每个请求之间传递200毫秒。
for(设i=0;ifailureshanded[i]);
}
const combined=Rx.Observable.combineLatest(缓冲);
组合的.first().subscribe(
(values)=>console.log(经过的()+):values:'+values.map(v=>formatResult(v)),
err=>console.log(err)
);

这种方法的问题是,所有的可观察对象一次被订阅,这可能会使网络连接过载。此外,如果一些可观察对象由于错误而从未发出next(),则生成的可观察对象永远不会完成。
如果不是所有的可观察对象都完成,我从来没有得到任何结果。
在这种情况下,您想做什么?一段时间后放弃?你的上传不应该在某个时候失败吗?另外,您声明不喜欢
combinelatetest
,因为在所有操作完成之前,您不会得到结果。您是否希望在每次完成后都得到一个结果?或者,无论是否发生任何变化,您希望每X秒得到一个结果吗?我想知道所有的观测值何时完成,无论它们是否用一个值“解析”。这个想法是要知道什么时候提供
const start = Date.now();

function elapsed() {
  return Date.now() - start;
}

function dummyObservable(delay, error) {
  // An observable that fails
  if (error) {
    return Rx.Observable.throw(error);
  }
  // An observable that succeeds after some amount of time
  if (delay) {
    return Rx.Observable.create(observer => {
      console.log(elapsed() + ': Request to server emitted');
      setTimeout(() => observer.next(), delay);
    });
  }
  // An observable that never completes (you really shouldn't have these)
  return Rx.Observable.create(() => {});
}

function formatResult(result) {
  if (result.failed) {
    return 'FAIL(' + result.error + ')';
  } else {
    return 'PASS';
  }
}

const obs1 = dummyObservable(1000);
const obs2 = dummyObservable(500);
const fails = dummyObservable(null, new Error('This one fails'));
const neverFinishes = dummyObservable();

const observables = [obs1, obs2, fails, neverFinishes];
// We only want the first response.  Only needed if your source observables aren't completing after emitting one item
const firstOnly = observables.map(obs => obs.first());
// Only allow 5 seconds and abort if no response after 5 seconds
const timeoutsHandled = firstOnly.map(obs => obs.timeout(5000));
// If any failures occur then handle them
const failuresHandled = timeoutsHandled.map(obs => obs.map(() => ({ failed: false })).catch((err) => Rx.Observable.of({ failed: true, error: err })));

const buffered = [];
// Buffer the request so 200 ms pass between each.
for(let i = 0; i < failuresHandled.length; i++) {
  const delay = i * 200;
  buffered.push(Rx.Observable.of([null]).delay(delay).first().switchMap(() => failuresHandled[i]));
}

const combined = Rx.Observable.combineLatest(buffered);
combined.first().subscribe(
  (values) => console.log(elapsed() + ': values: ' + values.map(v => formatResult(v))),
  err => console.log(err)
);