Как проверить, решено ли обещание Angular $q

Я понимаю, что обычно при использовании promises можно просто добавить код продолжения с вызовом then() и цепочкой.

Тем не менее, я хочу начать асинхронный вызов с завершением обещания, а затем отдельно запустить 3-секундный $timeout(), чтобы я мог принять действие пользовательского интерфейса, ТОЛЬКО, ЕСЛИ оригинальное обещание еще не завершено. (Я ожидаю, что это произойдет только при медленных соединениях, мобильных устройствах на 3G и т.д.).

Учитывая обещание, могу ли я проверить, завершено ли это или нет, без блокировки или ожидания?

Ответ 1

Я предполагаю, что это было добавлено в последней версии Angular, но, похоже, теперь есть объект состояния $$ по обещанию:

 var deferred = $q.defer();
 console.log(deferred.promise.$$state.status); // 0
 deferred.resolve();
 console.log(deferred.promise.$$state.status); //1 

Как отмечено в комментариях, это не рекомендуется, поскольку это может сломаться при обновлении версии Angular.

Ответ 2

Я думаю, что ваш лучший вариант, как есть (без изменения источника Angular и отправки запроса на перенос), заключается в том, чтобы сохранить локальный флаг, если обещание было разрешено. Reset это каждый раз, когда вы настраиваете обещание, которое вас интересует, и помечайте его как завершенное в then() для оригинального обещания. В $timeout then() установите флажок, чтобы узнать, разрешено ли обещание оригинала или нет.

Что-то вроде этого:

var promiseCompleted = false;
promise.then(function(){promiseCompleted=true;})
$timeout(...).then(function(){if(!promiseCompleted)doStuff()})

Внедрение Kris Kowal включает в себя другие методы проверки состояния обещания, но он выглядит Angular реализация $q, к сожалению, не включает их.

Ответ 3

Это кажется невозможным, как уже упоминал @shaunhusain. Но, возможно, это не обязательно:

// shows stuff from 3s ahead to promise completetion, 
// or does and undoes it in one step if promise completes before
$q.all(promise, $timeout(doStuff, 3000)).then(undoStuff);

или, может быть, лучше:

var tooSlow = $timeout(doStuff, 3000);
promise.always(tooSlow.cancel);

Ответ 4

У меня была аналогичная проблема, когда мне нужно было проверить, вернулось ли обещание. Поскольку функция AngularJS $watch регистрирует изменение при рендеринге страницы, даже если оба новых и старых значения undefined, я должен проверить, есть ли какие-либо данные, которые необходимо хранить в моей внешней модели.

Это, безусловно, взлом, но я делаю это:

$scope.$watch('userSelection', function() {
  if(promiseObject.hasOwnProperty("$$v"){
    userExportableState.selection = $scope.userSelection;
  }
};

Я знаю, что $$v - внутренняя переменная, используемая AngularJS, но она была достаточно надежной как индикатор разрешенного обещания для нас. Кто знает, что произойдет, когда мы перейдем к AngularJS 1.2: -/Я не вижу упоминания об улучшениях в $q в документах 1.2, но, возможно, кто-то напишет замену с лучшим набором функций ближе к Q.

Ответ 5

Я не знаю вашего точного сценария, но более типично устанавливать тайм-аут сразу после асинхронного вызова (и генерации обещания).

Предоставление инструкции setTimeout() в том же потоке событий, что и асинхронный вызов, вам не нужно беспокоиться о возможности действия гонки. Поскольку javascript строго одинарный, обещание .then() обратных вызовов гарантировано запускается в последнем потоке событий.