Overview

Angular-extend-promises

Have you ever dreamed of using Angular's promises like Bluebird's? With methods like map, join, filter, tap, …?

Now you can!

Setup

This library uses lodash as a dependency. Two versions are shipped:

  • one without lodash (it will require window._. This is your best choice if you already use lodash in your project)
  • one with an embedded custom build of lodash, only the parts needed, nothing more.

Install the library

With Bower:

bower install angular-extend-promises

With NPM:

npm install angular-extend-promises

Then, include the js file in your project:

<!-- if you already included lodash -->
<script src="bower_components/angular-extend-promises/angular-extend-promises-without-lodash.min.js" type="text/javascript"></script>

<!-- if you don't -->
<script src="bower_components/angular-extend-promises/angular-extend-promises.min.js" type="text/javascript"></script>

Replace bower_components by node_modules if necessary.

Finally, include the module in your app:

angular.module('my-project', [
  // [...]
  'angular-extend-promises'
])

And that's all! Angular's $q now contains all your favorite helpers!

Documentation

Core

On $q:

On promises:

Collections

On $q and on promises:

Utility

On promises:

Promisification

On promises

Timers

On promises:

Error management

On promises:

Error classes available:

Aliases

IE8 doesn't handle keywords as properties or method's names, so using .catch() will throw "SCRIPT1010: Expected identifier" for example.

You can use aliases in order not to use the ugly ['catch'](err) syntax.

  • $q.attempt -> $q.try
  • .caught -> .catch
  • .lastly -> .finally
  • .thenReturn and .returns -> .return
  • .thenThrow -> .throw

You can configure this behavior during the initialization of your app:

.config(['angularExtendPromisesProvider', function (angularExtendPromisesProvider) {
    angularExtendPromisesProvider.options.compatibilityAliases = false;
    angularExtendPromisesProvider.options.disableES5Methods = true;
}])

compatibilityAliases

The option compatibilityAliases is true by default.

You may want to disable aliases if your target browsers are ≥ IE9 (you don't need them).

disableES5Methods

The option disableES5Methods is false by default.

When true, it disables .catch(), .finally(), .try(), .return() and .throw().

This is a good way to prevent other contributors to use these methods when your app must run on IE ≤ 8.

$q.defer()

Example:

function thatReturnsAPromise() {
  var def = $q.defer();

  try {
    doAsyncStuff(function callback(result) {
      if (result.err)
        return def.reject(result.err);

      return def.resolve(result);
    });
  }
  catch (err) {
    def.reject(result.err);
  }

  return def.promise;
}

More documentation on Angular's project

$q.join(Promise|Thenable|value promises…, Function handler)

Example:

$q.join(getPictures(), getComments(), getTweets(),
  function(pictures, comments, tweets) {
    console.log('in total: ' + pictures.length + comments.length + tweets.length);
  }
);

More documentation on Bluebird's project

$q.method(Function method)

Example:

MyClass.prototype.method = Promise.method(function(input) {
  if (!this.isValid(input))
    throw new TypeError('input is not valid');

  if (this.cachedFor(input))
    return this.someCachedValue;

  return db.queryAsync(input).bind(this)
    .then(function(value) {
      this.someCachedValue = value;
      return value;
    })
  ;
});

More documentation on Bluebird's project

$q.reject(dynamic reason)

Example:

$q.reject('Just because.')
  .catch(function(theReason) {
    console.error(theReason);
  })
;

More documentation on Angular's project

$q.resolve(dynamic value)

Example:

$q.resolve(value)
  .then(function(resolvedValue) {
    console.log(resolvedValue);
  })
;

More documentation on Bluebird's project

$q.try(Function fn [, Array<dynamic>|dynamic argument] [, dynamic ctx])

Example:

function getUserById(id) {
  return $q.try(function() {
    if (!_.isNumber(id)) {
      throw new Error('id must be a number');
    }
    return db.getUserById(id);
  });
}

Alias: $q.attempt

More documentation on Bluebird's project

$q.when(dynamic value)

Example:

$q.when(somethingPossiblyAPromise)
  .then(function(resolvedValue) {
    console.log(resolvedValue);
  })
;

More documentation on Angular's project

.bind(dynamic thisArg)

Example:

somethingAsync().bind({})
  .spread(function (a, b) {
    this.a = a;
    this.b = b;
    return somethingElseAsync(a, b);
  })
  .then(function (c) {
    return this.a + this.b + c;
  });

More documentation on Bluebird's project

.catch(Function handler)

Example:

somePromise
  .then(function() {
    return a.b.c.d();
  })
  .catch(TypeError, function(e) {
    // If a is defined, will end up here because it is a type error to reference property of undefined
  })
  .catch(ReferenceError, function(e) {
    // Will end up here if a wasn't defined at all
  })
  .catch(MyCustomError1, MyCustomError2, function(e) {
    // Will end up here if one of the 2 defined above
  })
  .catch(function predicate(e) {
    return e.code >= 400
  }, function(e) {
    // Will end up here if e.code >= 400
  })
  .catch(MyCustomError3, function predicate(e) {
    return e.code < 300;
  }, function(e) {
    // You can mix predicates & error constructor to filter errors
  })
  .catch(function(e) {
    // Generic catch-the rest, error wasn't TypeError nor ReferenceError
  })
;

Alias: Promise.caught

More documentation on Angular's project and on Bluebird's project

.finally(Function handler)

Example:

$scope.jobRunning = true;
job.run(data)
  .then(function(output) {
    $scope.output = output;
  })
  .catch(errorHandler)
  .finally(function() {
    $scope.jobRunning = false;
  })
;

Alias: Promise.lastly

More documentation on Angular's project

.spread([Function fulfilledHandler] [, Function rejectedHandler ])

Example:

$q.all([somethingAsync1, somethingAsync2])
  .spread(function(result1, result2) {
    console.log(arguments);
  })
;

More documentation on Bluebird's project

.then([Function fulfilledHandler] [, Function rejectedHandler ])

Example:

$http.get(url)
  .then(function(result) {
    console.log(result.data);
  })
;

More documentation on Angular's project

.all()

Example:

$q.resolve(urls)
  .map($http.get)
  .all(function(results) {
    console.log(results.join('\n'));
  })
;

More documentation on Bluebird's project

.any()

Like .some(), with 1 as count.

However, if the promise fulfills, the fulfillment value is not an array of 1 but the value directly.

More documentation on Bluebird's project

.each(Function iterator)

Example:

$q.resolve(['http://some.url', urlPromise])
  .each(function(url, index, length) {
    return ping(url)
      .catch(function() {
        console.warn('Url ' + url + ' does not respond...');
      })
    ;
  })
;

More documentation on Bluebird's project

.filter(Function filterer [, Object options])

Example:

$q.resolve(values)
  // keep only even numbers
  .filter(function(val, i, length) {
    return !(val % 2); // you can also return a promise here
  })
;

More documentation on Bluebird's project

.map(Function mapper [, Object options])

Example:

$q.resolve(values)
  // square
  .map(function(val, index, length) {
    return val * val; // you can also return a promise here
  })
;

More documentation on Bluebird's project

.props()

Example:

promise.resolve({
  prop1: 42,
  prop2: somePromise
})
  .props()
  .then(function(val) {
    // val.prop === 42
    // val.prop2 is the resolveValue of somePromise
  })
;

More documentation on Bluebird's project

.reduce(Function reducer [, dynamic initialValue])

Example:

$q.resolve(values)
  // sum values
  .reduce(function(acc, val, index, length) {
    return acc + val; // you can also return a promise here
  }, 0)
;

More documentation on Bluebird's project

.settle()

Not implemented yet

More documentation on Bluebird's project

.some(int count)

Example:

promise.resolve([$q.resolve(42).delay(10), 21, $q.resolve(0).delay(20)])
  // get first x promises that resolve (ignore rejections)
  .some(2)
  .then(function(vals) {
    // vals === [21, 42]
  })
;

More documentation on Bluebird's project

.call(String propertyName [, dynamic arg...])

Example:

$q.resolve({
  method: function(arg1, arg2) {}
})
  // calls object's 'method' method with arg1 === 21 & arg2 === 42
  .call('method', 21, 42)
;

More documentation on Bluebird's project

.get(String propertyName)

Example:

$q.resolve({
  prop: 42
})
  .get('prop')
  // here value in then will be 42
;

$q.resolve([21, 42])
  .get(1)
  // here value in then will be 42
;

More documentation on Bluebird's project

.return(dynamic value)

Example:

$q.resolve(values)
  .return(42)
  // here value in then will be 42
;

Alias: Promise.returns & Promise.thenReturn

More documentation on Bluebird's project

.tap(Function handler)

Example:

$q.resolve(42)
  .tap(function(val) {
    console.log(val);
    return $q.reolve(21).delay(10);
  })
  // here value in then will be 42 but it will sync promise
  // on 21 and thus wait for the 10ms delay

More documentation on Bluebird's project

.throw(dynamic reason)

Example:

$q.resolve(42)
  .throw(new Error('toto'))
  // here the promise will coninue on its error flow with
  // the constructed error
;

Alias: Promise.thenThrow

More documentation on Bluebird's project

.nodeify([Function callback] [, Object options])

Example:

$q.resolve([1,2,3]).nodeify(function(err, result) {
  // err == null
  // result is the array [1,2,3]
});

$q.resolve([1,2,3]).nodeify(function(err, a, b, c) {
  // err == null
  // a == 1
  // b == 2
  // c == 3
}, {spread: true});

More documentation on Bluebird's project

.delay(int ms)

Example:

$q.resolve(42)
  // wait 10ms before going through with the previous value
  .delay(10)
  // here value will be 42 in then

More documentation on Bluebird's project

.timeout(int ms [, String message])

Example:

$q.resolve($q.resolve(42).delay(20))
  // fail if values do not resolve in 10ms max
  .timeout(10, 'optional message to construct the TimeoutError with')
  // thus here promise will be rejected with a $q.TImeoutError
;

More documentation on Bluebird's project

.done([Function fulfilledHandler] [, Function rejectedHandler ])

Example:

$q.reject(42)
  .done() // will throw 42 globally
;

$q.resolve(42)
  .done(function() {
    // will throw 42 globally
    return $q.reject(42);
  })
;

$q.resolve(42)
  .done(function() {
    throw 42; // will throw 42 globally
  })
;

$q.reject(42)
  .done(null, function() {}) // will not throw
;

More documentation on Bluebird's project

Tests

The library is tested as much as possible. Right now there are:

  • 60 unit tests
  • 90 functional tests

We probably still miss tests, PR are welcome!

You've seen a bug, you'd like a feature

Pull requests are welcome! (and an issue tracker is available)

License

License MIT