问题
I ran into an interesting issue today. I'm working on an app where we have file uploads, and we want to implement a progress bar. The app is written using React/Redux/Redux-Observable. I want to dispatch actions for upload progress. Here's what I did to implement it:
withProgress(method, url, body = {}, headers = {}) {
const progressSubscriber = Subscriber.create();
return {
Subscriber: progressSubscriber,
Request: this.ajax({ url, method, body, headers, progressSubscriber }),
};
}
I have a class that I use to make all my ajax requests. this.ajax
calls Observable.ajax
with the passed in parameters.
export const blobStorageUploadEpic = (action$) => {
return action$.ofType(a.BLOB_STORAGE_UPLOAD)
.mergeMap(({ payload }) => {
const { url, valetKey, blobId, blobData, contentType } = payload;
const { Subscriber, Request } = RxAjax.withProgress('PUT', `${url}?${valetKey}`, blobData, {
'x-ms-blob-type': 'BlockBlob',
'Content-Type': contentType,
});
const requestObservable = Request
.map(() => ({ type: a.BLOB_STORAGE_UPLOAD_SUCCESS, payload: { blobId } }))
.catch((err) => Observable.of({ type: a.BLOB_STORAGE_UPLOAD_FAILURE, err }));
return Observable.fromSubscriber(Subscriber)
.map((e) => ({ percentage: (e.loaded / e.total) * 100 }))
.map((percentage) => ({ type: a.BLOB_STORAGE_UPLOAD_PROGRESS, payload: { percentage} }))
.merge(requestObservable);
});
};
This is my epic. I get the subscriber back and I wrote a custom static method of Observable
to take in a subscriber. I then merge that with the Request
(which is an Observable
).
Observable.fromSubscriber = function fromSubscriber(externalSubscriber) {
return Observable.create((subscriber) => {
externalSubscriber.next = (val) => subscriber.next(val);
externalSubscriber.error = (err) => subscriber.error(err);
externalSubscriber.complete = () => subscriber.complete();
});
};
Finally, here is the custom static method I wrote on Observable
. I wrote this for two reasons. 1. As an example for anyone else dealing with a similar problem (I spent a lot of time trying to figure out how to make an Observable
from a Subscriber
before writing my own) and 2. To ask whether this is the best way to accomplish this goal. rxjs
is deep, and I figure that there's an existing way to do this, but I just couldn't find it.
回答1:
That is essentially what a Subject is for, the following should work as well:
export const blobStorageUploadEpic = (action$) => {
return action$.ofType(a.BLOB_STORAGE_UPLOAD)
.mergeMap(({ payload }) => {
const { url, valetKey, blobId, blobData, contentType } = payload;
const progressSubscriber = new Rx.Subject();
const request = Rx.Observable.ajax({
method: 'PUT',
url: `${url}?${valetKey}`,
body: blobData,
headers: {
'x-ms-blob-type': 'BlockBlob',
'Content-Type': contentType,
},
progressSubscriber
});
const requestObservable = request
.map(() => ({ type: a.BLOB_STORAGE_UPLOAD_SUCCESS, payload: { blobId } }))
.catch((err) => Observable.of({ type: a.BLOB_STORAGE_UPLOAD_FAILURE, err }));
return progressSubscriber
.map((e) => ({ percentage: (e.loaded / e.total) * 100 }))
.map((percentage) => ({ type: a.BLOB_STORAGE_UPLOAD_PROGRESS, payload: { percentage} }))
.merge(requestObservable);
});
};
Here is a more generic example (live @jsfiddle):
let data = "";
for (let c = 0; c < 100000; ++c) {
data += "" + Math.random();
}
const progressSubscriber = new Rx.Subject();
const request = Rx.Observable.ajax({
method: 'POST',
url: "/echo/json/",
body: JSON.stringify({ data }),
progressSubscriber
});
progressSubscriber
.merge(request)
.subscribe(console.log);
来源:https://stackoverflow.com/questions/41880983/implementing-fromsubscriber-in-rxjs