Заранее поймайте TimeoutError обещания

У меня есть обещание синей птицы, которое можно отменить. При отмене мне нужно проделать некоторую работу, чтобы аккуратно прервать текущую задачу. Задачу можно отменить двумя способами: через promise.cancel() или promise.timeout(delay).

Чтобы иметь возможность аккуратно прервать задачу при ее отмене или истечении времени ожидания, я должен поймать CancellationErrors и TimeoutErrors. Перехват CancellationError работает, но по какой-то причине я не могу поймать TimeoutError:

var Promise = require('bluebird');

function task() {
  return new Promise(function (resolve, reject) {
        // ... a long running task ...
      })
      .cancellable()
      .catch(Promise.CancellationError, function(error) {
        // ... must neatly abort the task ...
        console.log('Task cancelled', error);
      })
      .catch(Promise.TimeoutError, function(error) {
        // ... must neatly abort the task ...
        console.log('Task timed out', error);
      });
}

var promise = task();

//promise.cancel(); // this works fine, CancellationError is caught

promise.timeout(1000); // PROBLEM: this TimeoutError isn't caught!

Как отловить ошибки тайм-аута до его установки?


person Jos de Jong    schedule 08.05.2014    source источник


Ответы (1)


когда вы отменяете обещание, отмена сначала сообщается его родителям, пока находятся родители, которые все еще могут быть отменены, это сильно отличается от обычного отказа, который распространяется только на детей.

.timeout выполняет простое нормальное отклонение, а не отмену, поэтому сделать это таким образом невозможно.

Вы можете либо отменить после задержки:

var promise = task();
Promise.delay(1000).then(function() { promise.cancel(); });

или установите тайм-аут в функции задачи:

var promise = task(1000);

function task(timeout) {
  return new Promise(function (resolve, reject) {
        // ... a long running task ...
      })
      .timeout(timeout)
      .cancellable()
      .catch(Promise.CancellationError, function(error) {
        // ... must neatly abort the task ...
        console.log('Task cancelled', error);
      })
      .catch(Promise.TimeoutError, function(error) {
        // ... must neatly abort the task ...
        console.log('Task timed out', error);
      });
}

Вы также можете создать такой метод, как:

Promise.prototype.cancelAfter = function(ms) {
  var self = this;
  setTimeout(function() {
    self.cancel();
  }, ms);
  return this;
};

затем

function task() {
  return new Promise(function (resolve, reject) {
        // ... a long running task ...
      })
      .cancellable()
      .catch(Promise.CancellationError, function(error) {
        // ... must neatly abort the task ...
        console.log('Task cancelled', error);
      })
}

var promise = task();

// Since it's a cancellation, it will propagate upwards so you can
// clean up in the task function
promise.cancelAfter(1000);
person Esailija    schedule 08.05.2014
comment
Спасибо, я ожидал чего-то подобного. Проблема в том, что я не могу попросить пользователя аккуратно запустить promise.cancel() после использования тайм-аута для обещания, возвращаемого моим кодом. В этом случае единственный вариант, который я вижу, - это позволить пользователю указать (необязательный) тайм-аут при создании задачи, например promise = task([timeout]), а не выполнять promise.timeout(delay) впоследствии. - person Jos de Jong; 08.05.2014
comment
@JosdeJong хм, я видел, как вы написали в блоге getify, что у вас все еще есть эта проблема - я бы просто определил новый метод в прототипе Promise, см. Мое редактирование - person Esailija; 22.05.2014
comment
Привет Эсаилия, спасибо, что ответили мне. Ваша функция cancelAfter действительно именно то, что я искал, а не синие птицы timeout. Хотя я не любитель возиться с глобальными прототипами. Я думаю, у Кайла есть несколько хороших моментов. Существует серьезная разница между контролируемой задачей, которую я ищу (не связанной с цепочкой), и общим промисом, который можно связать в цепочку, разветвить и т. д. Мне имеет смысл обернуть необходимые мне функции управления в прототип задачи, внутренне используя обещания. Таким образом, Task является и Promise, и PromiseResolver. - person Jos de Jong; 22.05.2014
comment
@JosdeJong, это не глобальный прототип, прочитайте это github.com/petkaantonov/bluebird#for- авторы библиотек - person Esailija; 22.05.2014
comment
Да, это правда, так что эта проблема прекрасно решается с помощью bluebird. Тем не менее, решение с простым полифиллом ES6 Promise + тонкой оболочкой Task выглядит для меня лучше. Простота объяснения и обслуживания, четкое разделение задач и отсутствие привязки к конкретной реализации Promise. - person Jos de Jong; 22.05.2014
comment
@JosdeJong Я не понимаю, ты уже используешь bluebird? Кстати, метод timeout будет иметь более полезное поведение в версии 2.0: он отменит обещание с помощью TimeoutError (также отмена принимает аргумент для пользовательской причины отклонения). - person Esailija; 22.05.2014
comment
Это хорошая новость, это означает, что в bluebird v2.0 Promise.timeout действительно работает так, как я изначально ожидал :). Bluebird великолепен и чрезвычайно гибок, но также немного раздут. С удовольствием использую в нескольких проектах. Этот конкретный проект находится на ранней стадии, и я ищу лучшее решение. - person Jos de Jong; 22.05.2014
comment
@JosdeJong, да, вот коммит github.com/petkaantonov/bluebird/commit/ :) . В версии 2.0 будет удалено много лишнего (см. документацию WIP 2.0). ) и я планирую удалить некоторые совершенно бесполезные функции, например, прогресс . - person Esailija; 22.05.2014