S
S
sanex33392016-02-08 17:04:08
JavaScript
sanex3339, 2016-02-08 17:04:08

What is the strange behavior of Observable in conjunction with xhr.upload.onprogress?

Listen to my story.
I'm fiddling with Angular 2.
I made a service that sends files to the server with Ajax, at the same time counts percentages on the xhr.upload.onprogress event and writes it to its (service) sv-in.
I made another service that receives files from the form input, uploads them through the first service, and at the same time displays the download progress status. As you can see, it is mainly responsible for interacting with the view.
Well, of course, I wanted to try out Observable, so that the value of the onprogress property inside the 1st service (which, I remind you, is updated asynchronously in the onprogress event), monitor inside the 2nd service and immediately display it beautifully in the form of a bootstrap progressbar.
And here are the miracles. First code:
The 1st service that sends files by Ajax, an Observable object is created in it. I give only the constructor and the method of filling.

constructor () {
        this.progress$ = new Observable(observer => {
            this.progressObserver = observer
        }).share();
    }

private makeFileRequest (url: string, params: string[], files: File[]): Promise<any> {
        return new Promise((resolve, reject) => {
            let formData: FormData = new FormData(),
                xhr: XMLHttpRequest = new XMLHttpRequest();

            for (let i = 0; i < files.length; i++) {
                formData.append("uploads[]", files[i], files[i].name);
            }

            xhr.onreadystatechange = () => {
                if (xhr.readyState === 4) {
                    if (xhr.status === 200) {
                        resolve(JSON.parse(xhr.response));
                    } else {
                        reject(xhr.response);
                    }
                }
            };

            xhr.upload.onprogress = (event) => {
                this.progress = Math.round(event.loaded / event.total * 100);

                this.progressObserver.next(this.progress);
            };

            xhr.open('POST', url, true);
            xhr.send(formData);
        });
    }

2nd service, in which we monitor the progress $ from the 1st service:
this.fileUploadService.progress$.subscribe(progress => {
            this.uploadProgress = progress
        });

        this.fileUploadService.upload('/api/upload-file', [], this.file);

In general, with this code - as you may have guessed, nothing is updated at all.
I started looking and looking for the cause. First rendered this.progressObserver.next(this.progress); from the event to a higher level, in order to check the asynchrony, I shoved it into setInterval with a timeout of 500ms, and instead of the progress value, I turned on Math.random() * 100.
Everything became ok. The progress bar began to twitch to the beat of Math.random().
I put it back, you never know, it bugged me - nothing.
Well, somehow it happened that as a result of the tests I left this.progressObserver.next(this.progress); inside the xhr.upload.onprogress event handler, but setInterval didn't remove it, it only removed the body of its callback.
setInterval(() => {
}, 500);

And everything became as it should, the loading bar began to be updated, and not just anyhow, but at the setIterval interval.
The code turned out like this:
private makeFileRequest (url: string, params: string[], files: File[]): Promise<any> {
        return new Promise((resolve, reject) => {
            let formData: FormData = new FormData(),
                xhr: XMLHttpRequest = new XMLHttpRequest();

            for (let i = 0; i < files.length; i++) {
                formData.append("uploads[]", files[i], files[i].name);
            }

            xhr.onreadystatechange = () => {
                if (xhr.readyState === 4) {
                    if (xhr.status === 200) {
                        resolve(JSON.parse(xhr.response));
                    } else {
                        reject(xhr.response);
                    }
                }
            };

            setInterval(() => {
            }, 500);

            xhr.upload.onprogress = (event) => {
                this.progress = Math.round(event.loaded / event.total * 100);

                this.progressObserver.next(this.progress);
            };

            xhr.open('POST', url, true);
            xhr.send(formData);
        });
    }

Accordingly, as soon as I got such a result, I ran here and began to scribble this text.
Why is this happening?
Why without setInterval Observable inside onprogress doesn't return results correctly?
What's going on here anyway?

Answer the question

In order to leave comments, you need to log in

Didn't find what you were looking for?

Ask your question

Ask a Question

731 491 924 answers to any question