Слушайте мою историю.
Вожусь с ангуларом 2.
Сделал сервис, который аяксом отсылает файлы на сервер, заодно на событии xhr.upload.onprogress считает проценты и записывает в своё (сервиса) св-во.
Сделал еще другой сервис, который получает файлы из инпута формы, заливает их через первый сервис, а заодно и выводит состояние прогресса загрузки. Как видно, отвечает, в основном, за взаимодействие с вьюхой.
Ну разумеется, захотелось опробовать Observable, что бы за значением св-ва onprogress внутри 1-го сервиса (которое, напомню, обновляется асинхронно в эвенте onprogress), следить внутри 2-го сервиса и тут же красиво выводить в виде бутстраповского progressbar'а.
И вот тут то чудеса. Сначала код:
1-й сервис, который отправляет файлы аяксом, в нем создается Observable объект. Привожу только конструктор и метод заливки.
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);
});
}
2-й сервис, в котором следим за progress$ из 1-го сервиса:
this.fileUploadService.progress$.subscribe(progress => {
this.uploadProgress = progress
});
this.fileUploadService.upload('/api/upload-file', [], this.file);
В общем при таком коде - как вы уже догадались, ничего не обновляется, совсем.
Стал смотреть, да выискивать причину. Сначала вынес this.progressObserver.next(this.progress); из эвента на уровень выше, что бы проверить асинхронность пихнул в setInterval с таймаутом 500ms, а вместо значения progress врубил Math.random() * 100.
Стало все окей. Прогресс бар стал дергаться в такт Math.random().
Сунул обратно, мало ли, меня проглючило - ничего.
Ну и как то так получилось, что в результате тестов я оставил this.progressObserver.next(this.progress); внутри обработчика события xhr.upload.onprogress, но setInterval не удалил, удалил только тело его коллбэка.
setInterval(() => {
}, 500);
И все стало так как нужно, полоса загрузки стала обновляться, причем не абы как, а с интервалом setIterval.
Код получился вот такой:
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);
});
}
Соответственно, как только я получил такой результат - побежал сюда и стал строчить этот текст.
Почему так происходит?
Почему без setInterval Observable внутри onprogress не отдает корректно результаты?
Что вообще тут происходит?