import Promise from './promise'; import {

isFunction

} from './utils';

/**

`RSVP.map` is similar to JavaScript's native `map` method, except that it
 waits for all promises to become fulfilled before running the `mapFn` on
 each item in given to `promises`. `RSVP.map` returns a promise that will
 become fulfilled with the result of running `mapFn` on the values the promises
 become fulfilled with.

 For example:

 ```javascript

 var promise1 = RSVP.resolve(1);
 var promise2 = RSVP.resolve(2);
 var promise3 = RSVP.resolve(3);
 var promises = [ promise1, promise2, promise3 ];

 var mapFn = function(item){
   return item + 1;
 };

 RSVP.map(promises, mapFn).then(function(result){
   // result is [ 2, 3, 4 ]
 });
 ```

 If any of the `promises` given to `RSVP.map` are rejected, the first promise
 that is rejected will be given as an argument to the returned promise's
 rejection handler. For example:

 ```javascript
 var promise1 = RSVP.resolve(1);
 var promise2 = RSVP.reject(new Error('2'));
 var promise3 = RSVP.reject(new Error('3'));
 var promises = [ promise1, promise2, promise3 ];

 var mapFn = function(item){
   return item + 1;
 };

 RSVP.map(promises, mapFn).then(function(array){
   // Code here never runs because there are rejected promises!
 }, function(reason) {
   // reason.message === '2'
 });
 ```

 `RSVP.map` will also wait if a promise is returned from `mapFn`. For example,
 say you want to get all comments from a set of blog posts, but you need
 the blog posts first because they contain a url to those comments.

 ```javscript

 var mapFn = function(blogPost){
   // getComments does some ajax and returns an RSVP.Promise that is fulfilled
   // with some comments data
   return getComments(blogPost.comments_url);
 };

 // getBlogPosts does some ajax and returns an RSVP.Promise that is fulfilled
 // with some blog post data
 RSVP.map(getBlogPosts(), mapFn).then(function(comments){
   // comments is the result of asking the server for the comments
   // of all blog posts returned from getBlogPosts()
 });
 ```

 @method map
 @static
 @for RSVP
 @param {Array} promises
 @param {Function} mapFn function to be called on each fulfilled promise.
 @param {String} label optional string for labeling the promise.
 Useful for tooling.
 @return {Promise} promise that is fulfilled with the result of calling
 `mapFn` on each fulfilled promise or value when they become fulfilled.
  The promise will be rejected if any of the given `promises` become rejected.
 @static

*/ export default function map(promises, mapFn, label) {

return Promise.all(promises, label).then(function(values) {
  if (!isFunction(mapFn)) {
    throw new TypeError("You must pass a function as map's second argument.");
  }

  var length = values.length;
  var results = new Array(length);

  for (var i = 0; i < length; i++) {
    results[i] = mapFn(values[i]);
  }

  return Promise.all(results, label);
});

}