Continue the subscription after error

2019-05-17 11:41发布

问题:

I have a code where for each of the ids I am making an ajax request and processing them as results come in. Here is a simple replica of my actual code and jsfiddle:

var ids = [1,2,3,4,5,6];
var ids$ = (id) => {
    return Observable.of(id);
};
var loadIds$ = (id) => {
    if(id == 4) return xhr('/echo/jsoneee/', {id: id});
    return xhr('/echo/json/', {id: id});
};

Observable.from(ids)
.concatMap(id => Observable.forkJoin(ids$(id), loadIds$(id)))
.catch((err, caught) => {
  //get the id for which loadIds failed
  //send the id to subscribe() and continue the subscription
  return Observable.empty();
})
.subscribe(result => console.log(result))

But now I need to modify the code so that if an error occurs I will have to get the id for which the ajax request failed and then just continue the subscription like nothing happened. I have not been able to do this yet. Help is really appreciated.

回答1:

I think you can simplify this significantly by emitting correct values right in Observable.create(...):

function xhr(url, json) {
    return Observable.create(function (observer) {
        $.ajax({
            url: url,
            data: JSON.stringify(json),
            success: function (response) {
                observer.next([json.id, json]);
            },
            error: function (jqXHR, status, error) {
                observer.next([json.id]); // <== Notice this
            },
            complete: function () {
                observer.complete();
            }
        });
    });
}

var ids = [1,2,3,4,5,6];
var ids$ = (id) => {
    return Observable.of(id);
};
var loadIds$ = (id) => {
    if(id == 4) return xhr('/echo/jsoneee/', {id: id});
    return xhr('/echo/json/', {id: id});
};

Observable.from(ids)
    .concatMap(id => loadIds$(id))
    .subscribe(result => console.log(result));

This way you can avoid forkJoin() completely. Also be aware that catch() operator automatically unsubscribes from its source. This operator is intended to continue with another Observable so it's now very useful in cases such as yours.

You could of course use:

.catch((error, caught) => {
    return caught;
})

This however causes resubscription and thus reemission of all values from the beginning which is usually undesired.

There's also onErrorResumeNext() operator that simply ignores the errors but that's probably not what you want.

See demo: https://jsfiddle.net/4f1zmeyd/1/

A slightly similar question: get new ticket then retry first request