0

I would like to ask is it possible to return recursive factory method. I will show some code so you could understand me more. We have factory:

angular.module('module')
.factory('synchronizationStatus', ['$http', 'apiBase', '$timeout', function ($http, apiBase, $timeout) {
    var service_url = apiBase.url;

    function syncCompleted ( correlationId ) {
        checkSync (correlationId)
            .then(function(response){
                $timeout(function() {
                    // if I get response.data true, i want to proceed to controller
                    if(response.data){
                        return "now I want to return result to controller"
                    }
                    else {
                        // check again
                        checkSync(correlationId)
                    }
                }, 500);

            })
    }

    function checkSync( correlationId ){
        return $http.get(service_url + query);
    }

    return {
        syncCompleted: syncCompleted
    };
}]);

Main idea with this factory method is that i constantly (every 500ms each) send ajax request to backend and check if some operation is completed or no, and when it is completed i would like to send promise to controller function, which looks like this:

function save( client ) {
        clients.addClient( client )
            .then( function(response) {
              synchronizationStatus.syncCompleted(response.data.CorrelationId);
            }, onSaveError)
            .then( redirectToList, onSaveError )
            .finally( unblock );
    }

after backend returns true to my factory method i would like to execute other functions in my controller. Of course I could do recursion in my controller and it would solve this problem. Although I have to reuse this recursion in many other controllers so I would like to reuse this method.

Egizeris
  • 448
  • 1
  • 5
  • 19
  • That solution seems ok at first sight, what is the problem? Edit: ah right, the factory does not return a promise, one second, I have the solution :) – Jeroen Noten Mar 16 '15 at 15:46

2 Answers2

2

Yes, you should be able to do this, but you need to change the code in your factory a little:

angular.module('module')
.factory('synchronizationStatus', [
    '$http', 
    'apiBase', 
    '$timeout', 
    function ($http, apiBase, $timeout) {
        var service_url = apiBase.url;

        function waitSyncCompletion( correlationId ) {
            return checkSync (correlationId)
            .then(function(response){
                 if (response.data) {
                     return "all done!";
                 }   

                 return $timeout(function() {
                     // check again
                     return waitSyncCompletion(correlationId);
                 }, 500);
            });
        }

        function checkSync( correlationId ){
            var query = '...'; // determine query
            return $http.get(service_url + query);
        }

        return {
            waitSyncCompletion: waitSyncCompletion
        };
    }
]);

Then in your controller, you would need to use a return so that you can wait for the operation to complete:

function save( client ) {
    clients.addClient( client )
    .then( function(response) {
         return synchronizationStatus.waitSyncCompletion(response.data.CorrelationId);
    })
    .then(function (result) { console.log(result); })
    .then( redirectToList )
    .catch( onSaveError )
    .finally( unblock );
}
JLRishe
  • 99,490
  • 19
  • 131
  • 169
  • Thanks for helping. but that didn't solve my problem. "redirectToList" function is executed instantly after that: .then( function(response) { return synchronizationStatus.waitSyncCompletion(response.data.CorrelationId); }) – Egizeris Mar 16 '15 at 16:12
  • I want redirectToList() function to be executed instantly after waitSyncCompletion function returns: "all done!" – Egizeris Mar 16 '15 at 16:14
  • 2
    @Egizeris Did you try my factory modifications? The way your factory is written in your question, `syncCompleted` would finish immediately because it doesn't return anything. I strongly advise against using the deferred antipattern. It's well, you know, an antipattern. – JLRishe Mar 16 '15 at 16:14
  • Yes I tried and as I said after factory method it instantly executed redirectToList function. I want to stop controller execution till I get apropriate response from back end. Do you understand me, or should I try to explain with more code? – Egizeris Mar 16 '15 at 16:16
  • @Egizeris If you insert a `.then` with `console.log()` as I've added above, what does it log? – JLRishe Mar 16 '15 at 16:23
  • Ups sorry I did awful mistake. Instead of this: return synchronizationStatus.waitSyncCompletion(response.data.CorrelationId); I did this synchronizationStatus.waitSyncCompletion(response.data.CorrelationId); – Egizeris Mar 16 '15 at 16:26
  • everything works now as I wanted to work, thank you very much :) – Egizeris Mar 16 '15 at 16:26
-1

Make use of a custom deferred (promise), using Angular's $q. Make sure that you inject the $q dependency in your factory and create the deferred at the start of your factory:

var deferred = $q.defer()

At the point where the promise must resolve, add:

deferred.resolve();

And make sure you return the promise at the bottom of your syncCompleted function:

return deferred.promise;

Additionally, you could add deferred.reject() if you also want error-handling. Furthermore, you could add arguments to reject() and resolve() if you need to.

As an alternative, you could achieve the same goal without using $q. $timeout() also returns a promise, and if you return a promise in the callback given to then(), it will kind of 'replace' the 'parent' promise.

Jeroen Noten
  • 3,574
  • 1
  • 17
  • 25
  • Thanks, I thought about $q.defer() too, although I didn't know how to apply this properly. Your thoughts made it more clear. Thanks again, I will try this approach. – Egizeris Mar 16 '15 at 15:57
  • 1
    Sounds like you're just proposing the [deferred antipattern](http://stackoverflow.com/questions/23803743/what-is-the-deferred-antipattern-and-how-do-i-avoid-it). – JLRishe Mar 16 '15 at 15:57
  • You're right JLRishe, your solution is indeed nicer. – Jeroen Noten Mar 16 '15 at 15:58