Прогресс и отмена в асинхронных процессах
Дегтярев Евгений


Дерево задач
Прогресс в JQuery
function doSomething() {
var dfd = $.Deferred();
var count = 0;
var intervalId = setInterval(function() {
dfd.notify(count++);
count > 3 && clearInterval(intervalId);
}, 500);
return dfd.promise();
};
var promise = doSomething();
promise.progress(function(prog) {
console.log(prog);
});
Прогресс в Q
function requestOkText(url) {
return Q.Promise(function(resolve, reject, notify) {
var r = new XMLHttpRequest();
r.open("GET", url, true);
r.onload = ()=>resolve(r.responseText);;
r.onerror = ()=>reject(new Error("Can't XHR "));
r.onprogress = ()=>notify(event.loaded / event.total);
r.send();
});
}
return requestOkText("google.com").progress(progress => {
// We get notified of the upload's progress
});
Не пользуйтесь таким прогрессом
- Deprecated в библиотеках
- Сложно делать композицию
- Нет реализаций для Promise.all и Promise.map
- Много лишнего кода
- Несовместимо со стандартом Promises/A+
- Лок на конкретной реализации Promise
Правильный путь
function returnsPromiseWithProgress(progressHandler) {
return doFirstAction().then(function() {
progressHandler(0.33);
}).then(doSecondAction).then(function() {
progressHandler(0.66);
}).then(doThirdAction).then(function() {
progressHandler(1.00);
});
}
returnsPromiseWithProgress(function(progress) {
ui.progressbar.setWidth((progress * 200) + "px");
});
Использовать C# подход
Идеальный вариант
function returnsPromiseWithProgress(progress: IProgress) {
const firstActionProgress = progress.child();
const secondActionProgress = progress.child();
const thirdActionProgress = progress.child();
return doFirstAction(firstActionProgress)
.then(() => doSecondAction(secondActionProgress))
.then(() => doThirdAction(thirdActionProgress));
}
const progress = new Progress();
setInterval(function() {
ui.progressbar.set(progress.current());
}, 100);
returnsPromiseWithProgress(progress);
export interface IProgress {
current(): number;
add(value: number): void;
set(value: number): void;
complete(): void;
eliminate(): void;
child(weight?: number): IProgress;
}
Отмена
Отменить promise нельзя
Но если очень хочется то можно
Вариант 1. bluebird
const promise = doSomeWorkAsync();
cancelButton.onclick = function() {
promise.cancel();
};
bluebird 2
function makeCancellableRequest(url) {
var xhr = new XMLHttpRequest;
return new Promise(function (resolve, reject) {
xhr.addEventListener("error", reject);
xhr.addEventListener("load", resolve);
xhr.open("GET", url);
xhr.send(null);
})
.cancellable()
.catch(Promise.CancellationError, e => {
xhr.abort();
throw e;
});
}
bluebird 3
function makeCancellableRequest(url) {
return new Promise((resolve, reject, onCancel)=>{
var xhr = new XMLHttpRequest();
...
onCancel(function() {
xhr.abort();
});
});
}
Особенности
const promiseRoot = doSomeWork1();
const promiseChild1 = promiseRoot.then(doSomeWork2);
const promiseChild2 = promiseRoot.then(doSomeWork3);
promiseChild1.cancel();
Вариант 2. Rx
const subscription = new Rx.Subscription();
const promise = new Promise(resolve => {
const id = setTimeout(resolve, 1000);
subscription.add(() => clearTimeout(id));
});
promise.then(() => console.log('done'));
subcscription.unsubscribe();
Вариант 3.
Другой promsie
CancellationToken
function createToken() {
const token = {};
token.promise = new Promise(resolve => {
cancel = (reason) => {
token.reason = reason;
resolve(reason);
});
};
return { token, cancel };
}
const { token, cancel } = createToken();
function delay(ms, token) {
return new Promise(resolve => {
const id = setTimeout(resolve, ms);
token.promise.then((reason) => clearTimeout(id));
});
};
CancellationToken
Js progress and cancellation
By degtep
Js progress and cancellation
- 78