Blaž Zupančič
Blaž Zupančič

Reputation: 2356

How to abort an Ajax request from an Observable?

My code contains this simple function I'm using to upload files to my PHP server (there's an xhr request nested in an RxJS/Observable):

fileUpload(file: File): Observable<any> {
    return new Observable( observer => {
        let xhr:XMLHttpRequest = new XMLHttpRequest();
        xhr.onreadystatechange = () => {
            if (xhr.readyState === 4) {
                if (xhr.status === 200) {
                    observer.next(<any>JSON.parse(xhr.response));
                } else {
                    observer.error(xhr.response);
                    observer.complete();
                }
            }
        };

        xhr.open('POST', '__BACKEND__?action=file-upload', true);
        var formData = new FormData();
        formData.append('file', file, file.name);
        xhr.send(formData);
    });
}

It is completely functional but now I would also like to add some sort of a cancellation mechanic to it.

Just unsubscribing from the Observable won't work, because I need to somehow call xhr.abort() or I waste precious resources with large uploads.

Is it possible to get an elegant solution by modifying this code or am I doing it wrong because I'm using an RxJS/Observable for this task?

Upvotes: 4

Views: 2597

Answers (3)

Denis Derkach
Denis Derkach

Reputation: 391

Request will aborted into unsubscribe call. For more details, see AjaxObservable.unsubscribe sources: https://github.com/ReactiveX/rxjs/blob/441d52208df8b9247b01f8ca3993e3a7b0870b10/src/internal/observable/dom/AjaxObservable.ts#L423

Upvotes: 0

user5931608
user5931608

Reputation: 93

Return the xhr object and execute abort on it in a another observable.

var uploadObservable = fileUpload();
var uploadRequest;

uploadObservable.subscribe(
  function (x) {
    uploadRequest = x;
  },
  function (err) {
    console.log('Error: %s', err);
  },
  function () {
    console.log('Completed');
  });


var cancelBtn = Rx.Observable.fromEvent(cancelBtn, 'click');

cancelBtn.subscribe(
  function (x) {
    uploadRequest.abort();
  },
  function (err) {
    console.log('Error: %s', err);
  },
  function () {
    console.log('Completed');
  });

Or

 fileUpload()
    .flatMap(function(xhr) {
      Rx.Observable.fromEvent(cancelBtn, 'click').subscribe(function() {xhr.abort()})
    })
.subscribe(...);

Upvotes: 1

paulpdaniels
paulpdaniels

Reputation: 18663

When you create an Observable you can specify the unsubscribe behavior by returning a Subscription or a function from builder function:

fileUpload(file: File): Observable<any> {
    return new Observable( observer => {
        let xhr:XMLHttpRequest = new XMLHttpRequest();
        xhr.onreadystatechange = () => {
            if (xhr.readyState === 4) {
                if (xhr.status === 200) {
                    observer.next(<any>JSON.parse(xhr.response));
                    observer.complete();
                } else {
                    observer.error(xhr.response);
                }
            }
        };

        xhr.open('POST', '__BACKEND__?action=file-upload', true);
        var formData = new FormData();
        formData.append('file', file, file.name);
        xhr.send(formData);

        //Return the tear down logic. 
        //You may also want to check here that it has not already completed
        //Since this gets called in all cases when the `Subscription` terminates
        return () => xhr.abort();
    });
}

Upvotes: 9

Related Questions