Отмена
Отмена была переработана для bluebird 3.x. Любой код, зависящий от семантики отмены 2.x, не будет работать в 3.x.
Функция отмены по умолчанию выключена. Вы можете включить её, используя Promise.config.
Новая отмена использует семантику «не важно», в то время как старая отмена имела семантику прерывания. Отмена обещания просто означает, что его обработчики обратного вызова не будут вызваны.
Преимущества новой отмены по сравнению со старой:
.cancel()является синхронной.- не требуется дополнительный код для работы отмены
- совместима с другими функциями bluebird, такими как
Promise.all. - разумная семантика отмены для нескольких потребителей
В качестве оптимизации сигнал отмены распространяется вверх по цепочке обещаний, чтобы текущая операция, например, сетевой запрос, могла быть прервана. Однако, если сетевой запрос не прерывается, это не повлияет на работу, так как обратные вызовы все равно не вызываются.
Вы можете зарегистрировать необязательный обработчик отмены в корневом обещании, используя аргумент onCancel, передаваемый функции исполнителя при включенной отмене:
function makeCancellableRequest(url) {
return new Promise(function(resolve, reject, onCancel) {
var xhr = new XMLHttpRequest();
xhr.on("load", resolve);
xhr.on("error", reject);
xhr.open("GET", url, true);
xhr.send(null);
// Note the onCancel argument only exists if cancellation has been enabled!
onCancel(function() {
xhr.abort();
});
});
}
Обратите внимание, что обработчик onCancel — это действительно необязательная оптимизация, не связанная с работой отмены. Нет реальной необходимости регистрировать какие-либо обработчики отмены для работы отмены. Таким образом, любые ошибки, которые могут возникнуть во время выполнения обратного вызова onCancel, не обрабатываются и не превращаются в отклонения.
Хотя cancel(). является синхронной — onCancel() вызывается асинхронно (в следующем цикле) так же, как и обработчики then.
Пример:
var searchPromise = Promise.resolve(); // Dummy promise to avoid null check.
document.querySelector("#search-input").addEventListener("input", function() {
// The handlers of the previous request must not be called
searchPromise.cancel();
var url = "/search?term=" + encodeURIComponent(this.value.trim());
showSpinner();
searchPromise = makeCancellableRequest(url)
.then(function(results) {
return transformData(results);
})
.then(function(transformedData) {
document.querySelector("#search-results").innerHTML = transformedData;
})
.catch(function(e) {
document.querySelector("#search-results").innerHTML = renderErrorBox(e);
})
.finally(function() {
// This check is necessary because `.finally` handlers are always called.
if (!searchPromise.isCancelled()) {
hideSpinner();
}
});
});
Как показано в примере, обработчики, зарегистрированные с помощью .finally, вызываются даже если обещание отменено. Другим таким исключением является .reflect(). Никакие другие типы обработчиков не будут вызваны в случае отмены. Это означает, что в .then(onSuccess, onFailure) ни обработчик onSuccess, ни обработчик onFailure не вызываются. Это аналогично тому, как работает Generator#return — выполняются только активные finally блоки, а затем генератор завершает работу.
Что насчёт обещаний с несколькими потребителями?
Часто говорят, что обещания не могут быть отменены, так как они могут иметь нескольких потребителей.
Например:
var result = makeCancellableRequest(...); var firstConsumer = result.then(...); var secondConsumer = result.then(...);
Несмотря на то, что большинство пользователей обещаний никогда не будут использовать возможность прикрепления нескольких потребителей к обещанию, это всё же возможно. Проблема: «что должно произойти, если .cancel() вызывается на firstConsumer?» Распространение сигнала об отмене (и, следовательно, прерывание запроса) было бы очень плохим решением, так как второй потребитель может всё ещё быть заинтересован в результате, несмотря на потерю интереса со стороны первого потребителя.
На самом деле, result отслеживает количество потребителей (в данном случае 2) и прерывает запрос только в том случае, если все потребители сигнализируют об отмене. Однако, насколько firstConsumer может судить, обещание было успешно отменено, и его обработчики не будут вызваны.
Обратите внимание, что потребление уже отменённого обещания является ошибкой. В этом случае вы получите обещание, отклонённое с new CancellationError("late cancellation observer") в качестве причины отклонения.
© 2013–2018 Petka Antonov
Licensed under the MIT License.
http://bluebirdjs.com/docs/api/cancellation.html