A complete guide on $q and Promises in Angular

What is a promise in Angular?

  • A promise is a kind of Object that may be used to manage asynchronous tasks and can be used in production.
  • We call them promises because we’ve been “promised” a specific outcome at a later date.
  • For example, an HTTP call might take 200ms or 400ms to complete, but a promise will be performed once resolved.
  • There are three states for a promise: pending, settled, and rejected.

ES2015 Promises

  • Promise, as well as the resolve and reject arguments, are the most important elements here.

let promise = new Promise((resolve, reject) => {

if (/* some async task is all good */) {

      resolve(‘Success!’);

    } else {

      reject(‘Oops… something went wrong’);

    }

  });

  promise.then(data => {

    console.log(data);

  });

}

  • We just use new Promise(), and within it, we may execute an asynchronous activity, such as wrapping certain DOM events or even third-party libraries that are not promise Objects.
  • For example, we can wrap a pseudo third-party library called myCallbackLib() that provides us with a success and error callback inside a Promise and resolve and reject it as needed.

 const handleThirdPartyCallback = someArgument => {

    let promise = new Promise((resolve, reject) => {

      myCallbackLib(someArgument, response => {

        resolve(response);

      }, reason => {

          reject(reason);

      });

    });

    return promise;

  };

  handleThirdPartyCallback({ user: 101 }).then(data => {

    console.log(data);

  });

Angular framework offers magnificent libraries for seamless UI features development. To make development process easier, Angular offers RxJS library support and helps to compose asynchronous code effortlessly.

$q constructor

  • Because the $q implementation in AngularJS now has the same aspect as the native ES2015 promise Object, we can do the following:

let promise = $q((resolve, reject) => {

  if (/* some async task is all good */) {

    resolve(‘Success!’);

  } else {

    reject(‘Oops… something went wrong’);

  }

});

promise.then(data => {

  console.log(data);

});

The only difference from the previous example is that new promise() is replaced with $q().

  • Ideally, you’d deploy this in a service:

function MyService($q) {

  return {

    getSomething() {

      return $q((resolve, reject) => {

        if (/* some async task is all good */) {

          resolve(‘Success!’);

        } else {

          reject(‘Oops… something went wrong’);

        }

      });

    }

  };

}

angular

  .module(‘app’)

  .service(‘MyService’, MyService);

Then, as follows, it would be injected into a component controller:

const stuffComponent = {

  template: `

    <div>

      {{ $ctrl.stuff }}

    </div>

  `,

  controller(MyService) {

    this.stuff = [];

    MyService.getSomething()

      .then(data =&gt; this.stuff.unshift(data));

  }

};

angular

  .module(‘app’)

  .component(‘stuffComponent’, stuffComponent);

Alternatively, it may be mapped with a routing resolve as a binding property on a routed component.

const config = $stateProvider =&gt; {

  $stateProvider

    .state(‘stuff’, {

      url: ‘/stuff’,

      component: ‘stuffComponent’,

      resolve: {

        stuff: MyService =&gt; MyService.getSomething()

      }

    });

};

angular

  .module(‘app’)

  .config(config)

  .component(‘stuffComponent’, stuffComponent);

When should you use $q?

  • We’ve simply looked at a few pseudo-examples so far, and here’s our implementation of encapsulating the XMLHttpRequest Object into a promise-based solution; this should be the only actual reason(s) you make your own $q promises:

let getStuff = $q((resolve, reject) => {

  var xhr = new XMLHttpRequest();

  xhr.onreadystatechange = () => {

    if (xhr.readyState === 4) {

      if (xhr.status === 200) {

        resolve(JSON.parse(xhr.responseText));

      } else {

        reject(JSON.parse(xhr.responseText));

      }

    }

  };

  xhr.open(‘GET’, ‘/api/stuff’, true);

  xhr.send();

});

getStuff.then(data => {

  console.log(‘Boom!’, data);

});

  • Please note that this is not an endorsement of the XMLHttpRequest. Instead, use $http within Angular, which produces and returns a promise Object for you:

function getStuff() {

  return $http

    .get(‘/api/stuff’);

    .then(data => {

      console.log(‘Boom!’, data);

    });

}

getStuff().then(data => {

  console.log(‘Boom!’, data);

});

  • It indicates you should never do this because it just creates a new promise Object from an existing promise Object:

function getStuff() {

  let defer = $q.defer();

  $http

    .get(‘/api/stuff’);

    .then(response => {

      $q.resolve(response);

    }, reason => {

      $q.reject(reason);

    });

  return defer.promise;

}

getStuff().then(data => {

  console.log(‘Boom!’, data);

});

  • The golden rule is to utilize $q for non-promise items only. In this example, only generate your Promises; however, you may utilize additional methods like $q.all() and $q.race() in conjunction with other promises.

$q.defer()

  • Using $q.defer() as a Promise function Object() { [native code] } is simply another flavour, and the original implementation, of $q(). Assume the following code, which has been changed from the previous example of utilizing a service:

function MyService($q) {

  return {

    getSomething() {

      let defer = $q.defer();

      if (/* some async task is all good */) {

        defer.resolve(‘Success!’);

      } else {

        defer.reject(‘Oops… something went wrong’);

      }

      return defer.promise;

    }

  };

}

$q.when() / $q.resolve()

  • When you wish to resolve a promise from a non-promise Object, use $q.when() or $q.resolve() (they are identical, $q.resolve() is an alias for $q.resolve() to conform with ES2015 Promise naming rules), for example:

$q.when(123).then(res => {

  // 123

  console.log(res);

});

$q.resolve(123).then(res => {

  // 123

  console.log(res);

});

  • Note that $q.when() is also the same as $q.resolve().

$q.reject()

  • Using $q.reject() to reject a promise immediately is useful for things like HTTP Interceptors at the point of no return, since we can just return a rejected promise Object:

$httpProvider.interceptors.push($q => ({

  request(config) {…},

  requestError(config) {

    return $q.reject(config);

  },

  response(response) {…},

  responseError(response) {

    return $q.reject(response);

  }

}));

$q.all()

  • If you need to resolve multiple promises at once, you may use $q.all() to do so by handing in an Array or Object of promises to call. Once both have been resolved, call then () as follows:

let promiseOne = $http.get(‘/api/todos’);

let promiseTwo = $http.get(‘/api/comments’);

$q.all([promiseOne, promiseTwo]).then(data => {

  console.log(‘Both promises have resolved’, data);

});

$q.all({

    promiseOne,

    promiseTwo

  }).then(data => {

  console.log(‘Both promises have resolved’, data);

});

$q.race()

  • The $q.race() method is one of Angular’s newest additions, and it works similarly to $q.all(). However, the only response Object returned to you is whichever promise resolves first.
  • If API call 1 and API call 2 are both run at the same time and API call 2 resolves before API 1, the response Object will only include API call 2.

let promiseOne = $http.get(‘/api/todos’);

let promiseTwo = $http.get(‘/api/comments’);

$q.race([promiseOne, promiseTwo]).then(data => {

  console.log(‘Fastest wins, who will it be?…’, data);

});

$q.race({

    promiseOne,

    promiseTwo

  }).then(data => {

  console.log(‘Fastest wins, who will it be?…’, data);

});

Conclusion

In this article, we have learned in detail about $q and Promises in Angular. We utilize $q.all() and $q.race() to interact with existing promises and $q.all() to build promises for non-promise Objects/callbacks.

Author Bio:

Vinod Satapara ? Technical Director, iFour Technolab Pvt. Ltd.Technocrat and entrepreneur with years of experience building large scale enterprise web, cloud and mobile applications using latest technologies like Angular, ASP.NET, CORE, .NET MVC and Blockchain. Keen interest in addressing business problems using latest technologies and have been associated with an esteemed Excel Addin Development Company ? iFour Technolab Pvt. Ltd.