0

how can I run a function "searchBoats" with two parameters when these two parameters are returned as results from previous functions in AngularJS when these two functions are calling promise methods?

var parseURL = function() {                         
    var language = $routeParams.language;                           
    var url = $location.path(); 
    boatType = parseBoatType(url, language);
    var destination = parseDestination(url, language);
    searchBoats(destination, boatType);                                                                 };      

    parseURL();

    var parseBoatType = function(url, language) {
        var boatType = UrlService.parseUrlBoatType(url, language);                                      

        var boatTypeParsed = UrlService.parseBoatType(url, language);           

        // boat type is parsed
        if(boatTypeParsed) {
            BoatType.getBoatTypeByName({name: boatTypeParsed}, function success(result) {
                boatTypeAux = result;                   
                return boatTypeAux;
            });
        } 

        else {
            return null;
        }       
    };

    var parseDestination = function(url, language) {

        // departure is parsed
        var departure = UrlService.parseUrlDeparture(url);
        return $http.get("http://" + API_SERVER_URL + "/translatedDepartures?departure="+departure+";lang="+ language+";matchStart="+true).then(function(response) {                        
            departure = response.data.map(function(source) { 
                return source.element_translation; 
            });

            ...

Note: When I run BoatType.getBoatTypeByName in parseBoatType function the code is still running and I run searchBoats before I get the results.

Update:

searchBoats methods will look like:

var searchBoats = function(destination, boatType) { 

    Boat.query({
        destination: destination, 
        boatType: boatType
    }, function success(result) {
        console.log("getOptionList.query realizada");
        $scope.boats = result;           

        ...

According to your answer I have a callback BoatType.getBoatTypeByName({name: boatTypeParsed}, function success(result) { boatTypeAux = result; calling to a factory service to my api:

angular.factory('BoatType',
    function ($resource, SERVER_URL) {
        var boatTypes =
            $resource('http://' + SERVER_URL + '/:action', {
                action: 'boat_types'
            }, {
                query: {
                    method: 'GET',
                    isArray: true
                },
                getBoatTypeById: {
                    method: 'GET',
                    params: {
                        action: 'getBoatTypeById'
                    },
                    isArray: false
                },
                getBoatTypesById: {
                    method: 'GET',
                    params: {
                        action: 'getBoatTypesById'
                    },
                    isArray: true
                },
                getBoatTypeByName: {
                    method: 'GET',
                    params: {
                        action: 'getBoatTypeByName'
                    },
                    isArray: false
                }
            });
        return boatTypes;
    }
)

New update According to the comments of @zero298, now my code looks like this:

  var parseURL = function() {                           
        var language = $routeParams.language;                           
        var url = $location.path(); 

        // You need to wait on your async calls
        var boatsPromise = $q.all([
            parseBoatType(url, language),
            parseDestination(url, language)
        ]).then(function(resultArr){
            var boatType = resultArr[0],
                destination = resultArr[1];

            // Return something else to wait on
            return searchBoats(destination, boatType);
        });

   var parseBoatType = function(url, language) {

            BoatType.getBoatTypeByName({name: boatTypeParsed}, function success(result) {                   
                return result; 
            });
    };  

    var parseDestination = function(url, language) {            
        return "whatever";              
    };

// The service
.factory('BoatType', 

      function($resource, SERVER_URL){          
        var boatTypes =
         $resource('http://' + SERVER_URL +'/:action', {action:'boat_types'}, {       
            query: {method:'GET', isArray: true},                
            getBoatTypeByName: {method:'GET', params:{action: 'getBoatTypeByName'}, isArray: false}
         });        
         return boatTypes;           
      }
  )

Although, the function searchBoats waits until parseBoatType and parseDestination are executed, note parseBoatType has a callback to a service with a $resource call to an API (which I think is asynchnous), so as result, the searchBoats function is executed before the callback gets the result.

Rober
  • 5,868
  • 17
  • 58
  • 110
  • Is there any AJAX going on? Does `searchBoats` make an AJAX call? If it does, you need to make your code asynchronous and propogate up waiting for the request to finish. – zero298 Sep 19 '17 at 17:49
  • @zero298 could you please create an example? – Rober Sep 19 '17 at 17:59
  • I´m not sure about terminology but I think there is a promise in parseBoatType function. BoatType.getBoatTypeByName({name: boatTypeParsed}, function success(result) { – Rober Sep 19 '17 at 18:00
  • Show your `searchBoats` function. Are you using `$http` at all? – zero298 Sep 19 '17 at 18:00
  • @zero298 please, see updated comments. – Rober Sep 19 '17 at 18:03
  • new update with parseDestination there is $http there. @zero298 – Rober Sep 19 '17 at 18:05

1 Answers1

0

You aren't waiting on any of your asynchronous calls. Look at the documentation for $http and by extension $q.

You need to be doing something like this:

function parseURL() {
    var language = $routeParams.language;
    var url = $location.path();

    // You aren't waiting on anything that is async

    // You need to wait on your async calls
    var boatsPromise = $q.all([
        parseBoatType(url, language),
        parseDestination(url, language)
    ]).then(function(resultArr){
        var boatType = resultArr[0],
            destination = resultArr[1];

        // Return something else to wait on
        return searchBoats(destination, boatType);
    });

    // Either wait on it here and do some more stuff
    boatsPromise.then(console.log);

    // Or return it so that something higher in the callstack can wait
    return boatsPromise;
}

Your searchBoats function should also act like parseDestination and return a $q (Promise) to wait on:

function searchBoats(destination, boatType) {
    /*
     * I would recommend against putting stuff on to $scope
     * in an async chain unless you are at the very root
     * of the chain otherwise you lose context
     * and are unable to .then()
     */

    // Retun a then()able object
    return Boat.query({
            destination: destination,
            boatType: boatType
        },
        function success(result) {
            console.log("getOptionList.query realizada");
            return result;
        });
}
zero298
  • 25,467
  • 10
  • 75
  • 100
  • Thank you for the example, I understand what you try to do, but for some reason I´m still missing something because the searchBoats method is executed before I get the results of destinations and boatType. – Rober Sep 19 '17 at 19:07
  • The point is inside parseBoatType there is a callback calling to a service BoatType.getBoatTypeByName({name: boatTypeParsed}, function success(result) { boatTypeAux = result; and the value comes after the searhBoats method is called. – Rober Sep 19 '17 at 19:54
  • @Rober if `UrlService.parseBoatType` returns a `Promise` then you need to wait on that as well from within `parseBoatType`. – zero298 Sep 19 '17 at 19:56
  • Please, see my update in your answer. I don´t know how to defer this. – Rober Sep 19 '17 at 20:02
  • @Rober I have moved your edit into your question. I can't tell if you are using `$resource` correctly. I know that you can get the underlying Promise off of a resource using the `$promise` property. See the section right above [Creating a custom 'PUT' request](https://docs.angularjs.org/api/ngResource/service/$resource#creating-a-custom-put-request). You could probably use that. – zero298 Sep 19 '17 at 20:24
  • I have updated the question with your comments. I see your point but honestly don´t know where to put this promise. Could you please help with my new code above? – Rober Sep 19 '17 at 22:19
  • I mark your answer as right, because it´s the main matter of my question. However, I have created a new post with the second problem because I don´t know how to solve it. You can see it here: https://stackoverflow.com/questions/46329186/nesting-promises-with-resources-in-angularjs/46329317#46329317 – Rober Sep 21 '17 at 06:26
  • Please, see also this related post: https://stackoverflow.com/questions/46348382/angularjs-1-0-7-nesting-parallel-promises-with-resources – Rober Sep 21 '17 at 15:57