Uso de promesas en un servicio (factory)

Alexander A. Ramírez M.

En muchos casos es necesario acceder información de forma asíncrona. Hoy en día es posible hacer este trabajo mediante el uso de las promesas (promises).

Las promesas permiten definir servicios que no devuelven información inmediatamente y que se resuelven luego. Durante el flujo de la aplicación la promesa sirve para mandar a ejecutar una actividad y manejar su flujo luego que retorna, sin esperar por el resultado. Es decir, sigue la ejecución de la aplicación y cuando se resuelve la promesa (ya sea con éxito o error) entonces se ejecutan las acciones que dependen de la promesa.

Vamos a dar un ejemplo del uso de las promesas a través de un servicio (factory) para descargar información de Yahoo Finance.

Primero debemos declarar nuestro servicio (factory), denominado getYahooData, mediante el método factory en nuestro módulo de AngularJS. Adicionalmente debemos declarar el uso del servicio en nuestro controlador. En el servicio debemos inyectar $q y $http. El primero es para definir promesas y manejarlas y el segundo para acceder datos.

angular.module('app', ['ionic'])

.controller('appController', function($scope, getYahooData) {

})

.factory('getYahooData', function($q, $http) {

});

Ahora vamos a definir el método getTickerData. En el método declaremos la variable que vamos a usar para la promesa (deferred) y utilice el servicio $http para acceder a los datos de acuerdo al ejemplo previo en el blog. Les dejo la estructura típica como referencia. Básicamente el servicio $http también devuelve una promesa y se puede utilizar el método success y error de la promesa. Cada uno de esos métodos devuelve los datos si es exitoso o el error.

angular.module('app', ['ionic'])

.controller('appController', function($scope, getYahooData) {

})

.factory('getYahooData', function($q, $http) {
  
  var getTickerData = function(ticker) {
    var deferred = $q.defer();

    var url = 'http://finance.yahoo.com/webservice/v1/symbols/'+ ticker +'/quote?format=json&view=detail';

    $http.get(url)
      .success(function(json) {

      })
      .error(function(error){

      });

    return deferred.promise;
  };

});

Ahora defina lo que va a retornar el servicio, que es un objeto con la lista de métodos que se desean exponer en el servicio. El servicio debe retornar una estructura como esta siempre.

angular.module('app', ['ionic'])

.controller('appController', function($scope, getYahooData) {

})

.factory('getYahooData', function($q, $http) {
  
  var getTickerData = function(ticker) {
    var deferred = $q.defer();

    var url = 'http://finance.yahoo.com/webservice/v1/symbols/'+ ticker +'/quote?format=json&view=detail';

    $http.get(url)
      .success(function(json) {

      })
      .error(function(error){

      });

    return deferred.promise;
  };

  return {
    getTickerData: getTickerData
  };
});

Ahora vamos a implementar los métodos success y error utilizando los métodos resolve y reject. Adicionalmente el método getTickerData debe retornar la promesa.

angular.module('app', ['ionic'])

.controller('appController', function($scope, getYahooData) {

})

.factory('getYahooData', function($q, $http) {
  
  var getTickerData = function(ticker) {
    var deferred = $q.defer();
    var url = 'http://finance.yahoo.com/webservice/v1/symbols/'+ ticker +'/quote?format=json&view=detail';
    $http.get(url)
      .success(function(json) {
        console.log(json);
        var data = json.list.resources[0].resource.fields;
        deferred.resolve(data);
      })
      .error(function(error){
        console.log('getTickerData error ' + error);
        deferred.reject();
      });
    return deferred.promise;
  };

  return {
    getTickerData: getTickerData
  };
});

Ahora implemente el controlador, manejando la promesa y tomando el resultado utilizando el método then. Debe enviar el parámetro que espera el método y colocar en el modelo la información que viene del servicio.

angular.module('app', ['ionic'])

.controller('appController', function($scope, getYahooData) {
  var promise = getYahooData.getTickerData('AAPL');
  promise.then(function(data) {
    $scope.tickerData = data;
  });
})

.factory('getYahooData', function($q, $http) {
  
  var getTickerData = function(ticker) {
    var deferred = $q.defer();
    var url = 'http://finance.yahoo.com/webservice/v1/symbols/'+ ticker +'/quote?format=json&view=detail';
    $http.get(url)
      .success(function(json) {
        console.log(json);
        var data = json.list.resources[0].resource.fields;
        deferred.resolve(data);
      })
      .error(function(error){
        console.log('getTickerData error ' + error);
        deferred.reject();
      });
    return deferred.promise;
  };

  return {
    getTickerData: getTickerData
  };
});

Ahora ya cuenta con la estructura básica para manejar servicios con promesas. Esta es una herramienta muy importante para las aplicaciones web que obtienen sus datos principalmente de la nube y la mejor manera de utilizarlo es de forma asíncrona.

Como ejercicio defina un servicio para obtener datos sin acoplarlo a Yahoo Finance.

Espero les sirva. Les dejo el ejemplo en play.ionic.io.

Nota: Si leen la documentación se darán cuenta que los métodos success y error de $http ya están obsoletos. Así que el ejemplo sirve sólo para ilustrar el uso de $q.