3

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.

martin
  • 93,354
  • 25
  • 191
  • 226
lbrahim
  • 3,710
  • 12
  • 57
  • 95
  • What's this supposed to do `.concatMap(id => Observable.forkJoin(ids$(id), loadIds$(id)))`? – martin Nov 14 '16 at 12:37
  • @martin This is just a mock up to make things simpler. But it can be `forkJoin` or `zip` but the purpose is that `subscribe()` should have the `id` object and the response for that `id` ajax request as a single item together because I am doing other processing inside subscribe and I need both data. – lbrahim Nov 14 '16 at 12:45

1 Answers1

3

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

Community
  • 1
  • 1
martin
  • 93,354
  • 25
  • 191
  • 226
  • I would rather not have to modify `xhr()` according to this example. But I am not sure why this is working though as I want it to with `catch`? https://jsfiddle.net/ibrahimislam/ecLp6h1u/ – lbrahim Nov 14 '16 at 14:49
  • @lbrahim You won't get it working with `catch()` because it doesn't do what you want it to do. – martin Nov 14 '16 at 14:52
  • But if you will see this example: https://jsfiddle.net/ibrahimislam/ecLp6h1u. It is working as expected. – lbrahim Nov 16 '16 at 09:10
  • @lbrahim No, it's not the same. This just ignores the error and continues without including the failed id in the response. You'll get exactly the same result by using just `onErrorResumeNext()` without `catch()` at all – martin Nov 16 '16 at 09:18