使用rxjs

时间:2018-04-30 21:51:20

标签: angular rxjs

我想在Angular 5中上传一个文件数组(或者至少尝试上传,个别失败就行),一次一个,然后知道所有订阅的时间已经完成了。

在Javascript的旧时代,我会使用链式回调来完成这项工作,但由于我们现在拥有像rxjs这样的优秀工具,因此我感觉有一种更好,更具反应性的&#39 ;这样做的方法。

所以: rxJS最好的方法是什么?

我有一个Angular提供程序,它上传一个文件并返回一个Observable;我想尝试单独上传阵列中的每个文件,并知道它们何时完成。

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

问题:当我使用Observable.combineLatest()时,我只得到ALL Observables有next()ed结果的最终结果:[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]。如果不是所有的Observable都完成了,我根本就得不到结果。

此外,Observables不会一次运行一个,而是一次运行。当与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;
}

2 个答案:

答案 0 :(得分:0)

您可以使用combineLatest等待一组Observable完成。它会在每个Observable发出一次,它至少发出一次。

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'));

如果您希望Observables一个接一个地执行,您可以使用concat(已订购)或merge如果订单不重要。

为了捕获错误,您可以向每个Observable添加一个catch运算符,并返回一个空的Observable或更合适的东西。

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();
    });

答案 1 :(得分:0)

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

由于儿童可观察者的错误很好,你应该抓住它们。最后,如果您想缓冲这些项目,以便它们不会一次性发送到服务器,那么您也可以这样做。

全部放在一起(RxJS Playground Link):

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)
);