Angular JS: связывание обещаний и цикл дайджеста

ПРИМЕЧАНИЕ: скрипка использует старую версию Angular, и она больше не работает, потому что с версии 1.2 механизм шаблонов Angular не обрабатывает промисы прозрачно.

Я изучаю цепочку обещаний для заполнения моей области, а затем автоматически обновляю область действия dom.

Однако у меня возникают проблемы с этим. Если я вызываю «тогда» для уже разрешенного обещания, оно создает новое обещание (которое вызовет функцию успеха асинхронно, но почти сразу). Я думаю, проблема в том, что мы уже вышли из цикла дайджеста к моменту вызова функции успеха, поэтому дом никогда не обновляется.

Вот код:

<div ng-controller="MyCtrl">
    Hello, {{name}}! <br/>
    {{name2}}<br/>
    <button ng-click="go()">Clickme</button><br/>
    {{name3}}
</div>

var myApp = angular.module('myApp',[]);

function MyCtrl($scope, $q) {
    var data = $q.defer();    
    setTimeout(function() {$scope.$apply(data.resolve("Some Data"))}, 2000);
    var p = data.promise;

    $scope.name = p.then(angular.uppercase);
    $scope.name2 = p.then(function(x) { return "Hi "+x;});
    $scope.go = function() {
            $scope.name3 = p.then(function(x) { 
                // uncomment this to make it work:
                //$scope.$apply();
                return "Finally: "+x;
            });
    };
 }

http://jsfiddle.net/QZM4d/

Есть ли способ заставить это работать, не вызывая $apply каждый раз, когда я связываю обещания?


person Karen Zilles    schedule 02.02.2013    source источник
comment
Почему вы помещаете обещание в функцию go()?   -  person Plynx    schedule 02.02.2013
comment
Там есть обещание, потому что я запускаю код на основе исходного обещания p. Я хочу, чтобы код работал правильно, независимо от того, было ли p разрешено или нет... поэтому мы создаем новое обещание из старого обещания.   -  person Karen Zilles    schedule 04.02.2013


Ответы (1)


ПРИМЕЧАНИЕ: в скрипке используется старая версия Angular, и она больше не работает, потому что с версии 1.2 механизм шаблонов Angular не обрабатывает промисы прозрачно.

Чтобы цитировать @pkozlowski.opensource:

В AngularJS результаты разрешения промисов распространяются асинхронно внутри цикла $digest. Таким образом, обратные вызовы, зарегистрированные с помощью then(), будут вызываться только при входе в цикл $digest.

Итак, когда кнопка нажата, мы находимся в цикле дайджеста. then() создает новое обещание, но результаты этого then() не будут распространяться до следующего цикла дайджеста, который никогда не наступит (поскольку нет $timeout, $http или DOM событие, чтобы вызвать один). Если вы добавите еще одну кнопку с помощью ng-click, которая ничего не делает, а затем щелкните ее, это вызовет цикл дайджеста, и вы увидите результаты:

<button ng-click="">Force digest by clicking me</button><br/>

Вот скрипка, которая делает это.

Скрипка также использует $timeout вместо setTimeout — тогда $apply() не требуется.

Надеюсь, понятно, когда вам нужно использовать $apply. Иногда вам нужно вызвать его вручную.

person Mark Rajcok    schedule 02.02.2013
comment
Ладно, думаю, теперь я все понял. В итоге я переписывал свой вопрос и код так много раз, что около 80% пути я сделал сам. Плохо то, что нам приходится вручную принудительно выполнять дайджест для разрешения промисов, но кодировать таким образом все же может быть интересно. - person Karen Zilles; 04.02.2013
comment
Я считаю, что вы можете использовать обещания $http или $q, чтобы после вызовов происходил цикл дайджеста angular. Я сейчас в поиске лучшего способа. - person Oak; 02.05.2014
comment
Обратите внимание, что скрипка использует старую версию Angular и что она больше не работает, потому что с версии 1.2 механизм шаблонов Angular не обрабатывает промисы прозрачно. - person user276648; 27.11.2014
comment
Добавление кнопки, которая ничего не делает, будет работать, но почему двойное нажатие ng-click=go не запускает цикл дайджеста? - person axings; 29.11.2016
comment
@axings, что такое go? Если это недопустимое выражение Angular, то цикл дайджеста, вероятно, не запустится. - person Mark Rajcok; 29.11.2016
comment
Извините, @MarkRajcok, я имел в виду ng-click=go(), согласно этому ответу нажатие кнопки запускает цикл дайджеста, тогда почему нажатие кнопки ‹button ng-click=›Принудительно дайджест, нажав меня‹/button›‹br/› будет действительно так, но нажатие кнопки ng-click=go()›Clickme‹/button›‹br/› во второй раз не работает? - person axings; 06.12.2016
comment
›› кажется слишком плохим, что мы должны вручную принудительно выполнять дайджест для разрешения промисов. Согласен. - person SamFlushing; 10.03.2017