Обещание цепочки при использовании $timeout

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

Но вместо ABAB все время ABBA.

Как я могу использовать API-интерфейс обещания, чтобы гарантировать, что длительный вызов (или отложенный вызов с использованием $timeout) действительно завершен до того, как .then() будет выполнено?

Код

angular
  .module('app', [])
  .controller('ThenCtrl', ThenCtrl);

function ThenCtrl($timeout, $q) {
  var vm = this;

  vm.items = [];

  $q.when(pushA()).then(pushB());

  $timeout(pushA, 5000).then(pushB());

  function pushA() {
    vm.items.push('A');
  }

  function pushB() {
    vm.items.push('B');
  }
}

Разметка

<div ng-app="app">
  <div ng-controller="ThenCtrl as vm">
    {{vm.items}}
  </div>
</div>

Я настроил скрипт: https://jsfiddle.net/kan3c61t/


person twip    schedule 08.03.2016    source источник


Ответы (3)


Не вызывайте функции внутри методов .then.

  ̶$̶q̶.̶w̶h̶e̶n̶(̶p̶u̶s̶h̶A̶(̶)̶)̶.̶t̶h̶e̶n̶(̶p̶u̶s̶h̶B̶(̶)̶)̶;̶
  $q.when(pushA()).then(pushB);

  ̶$̶t̶i̶m̶e̶o̶u̶t̶(̶p̶u̶s̶h̶A̶,̶ ̶5̶0̶0̶0̶)̶.̶t̶h̶e̶n̶(̶p̶u̶s̶h̶B̶(̶)̶)̶;̶    
  $timeout(pushA, 5000).then(pushB);

Вместо этого передайте функции в качестве аргументов методу .then. Служба $q будет содержать эти функции для последующего вызова.

Работа службы $q заключается в том, что она сохраняет аргумент метода .then как функцию, которая будет вызываться позже. В этом случае служба $q сохраняла значение, возвращенное pushB(), с побочным эффектом немедленного помещения B в массив.

ДЕМО на JSFiddle

person georgeawg    schedule 08.03.2016
comment
Тоже довольно интересное решение. - person Satej S; 08.03.2016
comment
Очень четко сказано. Какое значение могут иметь эти вводные пары. - person twip; 08.03.2016
comment
Это мне очень помогло - person Fergus; 26.08.2017
comment
Как насчет функций с несколькими аргументами? - person firstpostcommenter; 22.01.2021
comment
Прозрачный. Но мне нужно вызвать функцию function1, а затем вызвать другую функцию function2 после разрешения function1, потому что мне нужен результат function1. Но function2 также имеет некоторые другие дополнительные аргументы, которые доступны в файле .js.... Возможно, мне нужно также отправить эти значения в функцию1 и заставить функцию1 вернуть результат, который также имеет эти переменные, которые затем можно использовать в функции2? - person firstpostcommenter; 23.01.2021
comment
@firstpostcommenter Пожалуйста, задайте это как новый вопрос. Раздел комментариев следует использовать только для уточнения существующего ответа. Продолжительные рассуждения здесь неуместны. Также посмотрите на порядок выполнения AngularJS с $q — связывание обещаний - person georgeawg; 23.01.2021

Ну вот. Что я сделал, так это добавил функцию success в часть кода then.

$timeout(pushA, 5000).then(function(success) {
    pushB()
  });

Вот рабочая демонстрация.

Вы также можете добавить error function вот так

 $timeout(pushA, 5000).then(function(success) {
    pushB()
  },function(error){console.log("Error");});

В поисках этого ответа я также наткнулся на эту полезную ссылку.

person Satej S    schedule 08.03.2016
comment
Это отличное напоминание о базовой структуре API; благодарю вас. - person twip; 08.03.2016

Как уже упоминалось, ваша большая проблема в том, что вы .then(promise), а не .then(function).

Обещания представляют ценность + время. Это результат уже начатой ​​операции. Обещание — это значение — then ожидает функции. Вы не можете «запустить промис после другого промиса» — поскольку промис означает, что операция уже началась.

Когда вы then(x) выполняете что-либо, кроме функции, это игнорируется. Это неудачный выбор в спецификации промисов, но нам придется с этим смириться.

Поскольку ваши вызовы синхронны, вы не должны использовать для этого промисы. Если ваш код делает что-то синхронное, вы можете упорядочивать действия с помощью ;, а не then:

pushA();
pushB(); 

Если это вызов, который возвращает обещания, он просто становится:

pushA().then(pushB);

Нет смысла вызывать $q.when, который преобразует не промисы в промисы.

Я бы написал так:

pushA();
$timeout(5000).then(pushB); 

Нет смысла преобразовывать первое синхронное действие в функцию, возвращающую промис, или задействовать промисы где-либо, кроме тайм-аута. Если вам нужно, чтобы pushA произошло через 5000 мс, я бы все равно, вероятно, написал:

$timeout(5000).then(pushA).then(pushB)

Так как я думаю, что это более читабельно, и опять же, мы не задействуем pushA и pushB с промисами напрямую.

person Benjamin Gruenbaum    schedule 08.03.2016
comment
Спасибо за ответ. Иногда очень сложно хорошо уловить намерение, будучи кратким и ясным для постов SO. То, что я пытался зафиксировать, — это сценарий, в котором мы предполагаем, что A будет разрешено до B, а A — это обещание, которое должно нести задержку. Значение: я хотел бы, чтобы pushA завершил свою работу, включая задержку, и только потом выполнил pushB. Вот обновленная скрипта, реализованная с вашим участием, которая помогла мне прийти к тому, что я искал: jsfiddle.net/ nam3cbaw/1 - person twip; 08.03.2016