Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to return Observable from xhr

I have function like this, it just upload image with xhr request:

public uploadAvatar(avatarFile, callback) {
    return new Promise((resolve, reject) => {
      let formData: any = new FormData();
      let url = 'avatar';
      let imgArr = "avatar";
      let xhr = new XMLHttpRequest();

      formData.append(imgArr, avatarFile, avatarFile.name);
      xhr.onreadystatechange = function () {
        if (xhr.readyState == 4) {
          if (xhr.status == 200) {
            resolve(JSON.parse(xhr.response));
            callback('success');
          } else {
            reject(xhr.response);
            callback('error');
          }
        }
      };

      xhr.open("POST", environment.baseUrl + url, true);
      xhr.setRequestHeader('Auth', this._auth.getToken());
      xhr.send(formData);
    });
  }

How can I return Observable insted Promise?

like image 631
Vladimir Avatar asked Feb 04 '23 10:02

Vladimir


2 Answers

eddyP23's answer is almost correct but there are two more things.

One key difference between Observables and Promises is that Observables can be canceled in contrast to Promises. This is very relevant to your situation where you're using XHR to perform AJAX calls. While XMLHttpRequest can be aborted using the abort() function you can't abort pending Promise.

However with Observables you can. When you complete or unsubscribe an Observable you can also cancel the XHR object because you know you're not interested in it.

See also: https://medium.com/@benlesh/promise-cancellation-is-dead-long-live-promise-cancellation-c6601f1f5082

So you function when transformed from using Promises to Observables should look like this:

public uploadAvatar(avatarFile, callback) {
    return Rx.Observable.create(function (observer) {
        ...
        xhr.onreadystatechange = function () {
        if (xhr.readyState == 4) {
            if (xhr.status == 200) {
                observer.onNext(JSON.parse(xhr.response));
                observer.onCompleted();
            } else {
                observer.onError(xhr.response);
            }
        }

        return () => {
            xhr.abort();
        };
    }
)

Notice that we're returning a function which is used when disposing the Observable. For more information about Observable.create have a look at http://reactivex.io/rxjs/class/es6/Observable.js~Observable.html#static-method-create

The second thing is that if you don't care about aborting the XHR you can just use Promises. RxJS 5 treats Promises the same way as Observables. Notice that Observable.from() takes as a parameter some ObservableInput which is in detail described here: http://reactivex.io/rxjs/class/es6/MiscJSDoc.js~ObservableInputDoc.html

For you it means that you can just call uploadAvatar() as is and the Promise returned is going to be turned into an Observables automatically:

Observable.from(uploadAvatar(...))
    .subscribe(...)
like image 103
martin Avatar answered Feb 13 '23 16:02

martin


Something like this should work

 public uploadAvatar(avatarFile, callback) {
    return Rx.Observable.create(function (observer) {
      let formData: any = new FormData();
      let url = 'avatar';
      let imgArr = "avatar";
      let xhr = new XMLHttpRequest();

      formData.append(imgArr, avatarFile, avatarFile.name);
      xhr.onreadystatechange = function () {
        if (xhr.readyState == 4) {
          if (xhr.status == 200) {
            observer.onNext(JSON.parse(xhr.response));
            observer.onCompleted();
            callback('success');
          } else {
            observer.onError(xhr.response);
            callback('error');
          }
        }
      };

      xhr.open("POST", environment.baseUrl + url, true);
      xhr.setRequestHeader('Auth', this._auth.getToken());
      xhr.send(formData);
    });
}
like image 44
eddyP23 Avatar answered Feb 13 '23 16:02

eddyP23