Skip to content

Commit

Permalink
Fix #821
Browse files Browse the repository at this point in the history
  • Loading branch information
petkaantonov committed Oct 28, 2015
1 parent 48f73f0 commit 526183e
Show file tree
Hide file tree
Showing 8 changed files with 139 additions and 39 deletions.
2 changes: 2 additions & 0 deletions docs/docs/api-reference.md
Expand Up @@ -38,6 +38,7 @@ redirect_from: "/docs/api/index.html"
- [Promise.reduce](api/promise.reduce.html)
- [Promise.filter](api/promise.filter.html)
- [Promise.each](api/promise.each.html)
- [Promise.mapSeries](api/promise.mapseries.html)
- [Promise.race](api/promise.race.html)
- [.all](api/all.html)
- [.props](api/props.html)
Expand All @@ -47,6 +48,7 @@ redirect_from: "/docs/api/index.html"
- [.reduce](api/reduce.html)
- [.filter](api/filter.html)
- [.each](api/each.html)
- [.mapSeries](api/mapseries.html)
- [Resource management](api/resource-management.html)
- [Promise.using](api/promise.using.html)
- [.disposer](api/disposer.html)
Expand Down
25 changes: 21 additions & 4 deletions docs/docs/api/each.md
Expand Up @@ -12,23 +12,40 @@ title: .each
```js
.each(function(any item, int index, int length) iterator) -> Promise
```

Iterate over an array, or a promise of an array, which contains promises (or a mix of promises and values) with the given `iterator` function with the signature `(value, index, length)` where `value` is the resolved value of a respective promise in the input array. Iteration happens serially. If any promise in the input array is rejected the returned promise is rejected as well.

Resolves to the original array unmodified, this method is meant to be used for side effects. If the iterator function returns a promise or a thenable, then the result of the promise is awaited, before continuing with next iteration.

Example where you might want to utilize `.each`:

```js
.mapSeries(function(any item, int index, int length) mapper) -> Promise
// Source: http://jakearchibald.com/2014/es7-async-functions/
function loadStory() {
return getJSON('story.json')
.then(function(story) {
addHtmlToPage(story.heading);
return story.chapterURLs.map(getJSON);
})
.each(function(chapter) { addHtmlToPage(chapter.html); })
.then(function() { addTextToPage("All done"); })
.catch(function(err) { addTextToPage("Argh, broken: " + err.message); })
.then(function() { document.querySelector('.spinner').style.display = 'none'; });
}
```
Same as [Promise.each(this, iterator)](.).
</markdown></div>
<div id="disqus_thread"></div>
<script type="text/javascript">
var disqus_title = ".each";
var disqus_shortname = "bluebirdjs";
var disqus_identifier = "disqus-id-each";
(function() {
var dsq = document.createElement("script"); dsq.type = "text/javascript"; dsq.async = true;
dsq.src = "//" + disqus_shortname + ".disqus.com/embed.js";
(document.getElementsByTagName("head")[0] || document.getElementsByTagName("body")[0]).appendChild(dsq);
})();
</script>
<noscript>Please enable JavaScript to view the <a href="https://disqus.com/?ref_noscript" rel="nofollow">comments powered by Disqus.</a></noscript>
<noscript>Please enable JavaScript to view the <a href="https://disqus.com/?ref_noscript" rel="nofollow">comments powered by Disqus.</a></noscript>
31 changes: 31 additions & 0 deletions docs/docs/api/mapseries.md
@@ -0,0 +1,31 @@
---
layout: api
id: mapseries
title: .mapseries
---


[← Back To API Reference](/docs/api-reference.html)
<div class="api-code-section"><markdown>
##.mapSeries

```js
.mapSeries(function(any item, int index, int length) mapper) -> Promise
```

Same as [Promise.mapSeries(this, iterator)](.).
</markdown></div>

<div id="disqus_thread"></div>
<script type="text/javascript">
var disqus_title = ".mapSeries";
var disqus_shortname = "bluebirdjs";
var disqus_identifier = "disqus-id-mapSeries";

(function() {
var dsq = document.createElement("script"); dsq.type = "text/javascript"; dsq.async = true;
dsq.src = "//" + disqus_shortname + ".disqus.com/embed.js";
(document.getElementsByTagName("head")[0] || document.getElementsByTagName("body")[0]).appendChild(dsq);
})();
</script>
<noscript>Please enable JavaScript to view the <a href="https://disqus.com/?ref_noscript" rel="nofollow">comments powered by Disqus.</a></noscript>
35 changes: 6 additions & 29 deletions docs/docs/api/promise.each.md
Expand Up @@ -5,6 +5,7 @@ title: Promise.each
---



[← Back To API Reference](/docs/api-reference.html)
<div class="api-code-section"><markdown>
##Promise.each
Expand All @@ -15,37 +16,13 @@ Promise.each(
function(any item, int index, int length) iterator
) -> Promise
```
```js
Promise.mapSeries(
Iterable<any>|Promise<Iterable<any>> input,
function(any item, int index, int length) mapper
) -> Promise
```

Given an [`Iterable`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Iteration_protocols)\(arrays are `Iterable`\), or a promise of an `Iterable`, which produces promises (or a mix of promises and values), iterate over all the values in the `Iterable` into an array and iterate over the array serially, in-order.
[api/promise.each](unfinished-article)

Returns a promise for an array that contains the values returned by the `iterator` function in their respective positions. The iterator won't be called for an item until its previous item, and the promise returned by the iterator for that item are fulfilled. This results in a `mapSeries` kind of utility but it can also be used simply as a side effect iterator similar to [`Array#forEach`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/forEach).
Iterate over an array, or a promise of an array, which contains promises (or a mix of promises and values) with the given `iterator` function with the signature `(value, index, length)` where `value` is the resolved value of a respective promise in the input array. Iteration happens serially. If any promise in the input array is rejected the returned promise is rejected as well.

If any promise in the input array is rejected or any promise returned by the iterator function is rejected, the result will be rejected as well.
Resolves to the original array unmodified, this method is meant to be used for side effects. If the iterator function returns a promise or a thenable, then the result of the promise is awaited, before continuing with next iteration.

Example where [.each](.)\(the instance method\) is used for iterating with side effects:

```js
// Source: http://jakearchibald.com/2014/es7-async-functions/
function loadStory() {
return getJSON('story.json')
.then(function(story) {
addHtmlToPage(story.heading);
return story.chapterURLs.map(getJSON);
})
.each(function(chapter) { addHtmlToPage(chapter.html); })
.then(function() { addTextToPage("All done"); })
.catch(function(err) { addTextToPage("Argh, broken: " + err.message); })
.then(function() { document.querySelector('.spinner').style.display = 'none'; });
}
```
The alias `mapSeries` is provided for an opportunity to be more explicit when using `.each` as a linear mapping operation instead of a side effect iterator.

<hr>
</markdown></div>
Expand All @@ -55,11 +32,11 @@ The alias `mapSeries` is provided for an opportunity to be more explicit when us
var disqus_title = "Promise.each";
var disqus_shortname = "bluebirdjs";
var disqus_identifier = "disqus-id-promise.each";

(function() {
var dsq = document.createElement("script"); dsq.type = "text/javascript"; dsq.async = true;
dsq.src = "//" + disqus_shortname + ".disqus.com/embed.js";
(document.getElementsByTagName("head")[0] || document.getElementsByTagName("body")[0]).appendChild(dsq);
})();
</script>
<noscript>Please enable JavaScript to view the <a href="https://disqus.com/?ref_noscript" rel="nofollow">comments powered by Disqus.</a></noscript>
<noscript>Please enable JavaScript to view the <a href="https://disqus.com/?ref_noscript" rel="nofollow">comments powered by Disqus.</a></noscript>
57 changes: 57 additions & 0 deletions docs/docs/api/promise.mapseries.md
@@ -0,0 +1,57 @@
---
layout: api
id: promise.mapseries
title: Promise.mapSeries
---


[← Back To API Reference](/docs/api-reference.html)
<div class="api-code-section"><markdown>
##Promise.mapSeries

```js
Promise.mapSeries(
Iterable<any>|Promise<Iterable<any>> input,
function(any item, int index, int length) mapper
) -> Promise
```

Given an [`Iterable`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Iteration_protocols)\(arrays are `Iterable`\), or a promise of an `Iterable`, which produces promises (or a mix of promises and values), iterate over all the values in the `Iterable` into an array and iterate over the array serially, in-order.

Returns a promise for an array that contains the values returned by the `iterator` function in their respective positions. The iterator won't be called for an item until its previous item, and the promise returned by the iterator for that item are fulfilled. This results in a `mapSeries` kind of utility but it can also be used simply as a side effect iterator similar to [`Array#forEach`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/forEach).

If any promise in the input array is rejected or any promise returned by the iterator function is rejected, the result will be rejected as well.

Example where [.mapSeries](.)\(the instance method\) is used for iterating with side effects:

```js
// Source: http://jakearchibald.com/2014/es7-async-functions/
function loadStory() {
return getJSON('story.json')
.then(function(story) {
addHtmlToPage(story.heading);
return story.chapterURLs.map(getJSON);
})
.mapSeries(function(chapter) { addHtmlToPage(chapter.html); })
.then(function() { addTextToPage("All done"); })
.catch(function(err) { addTextToPage("Argh, broken: " + err.message); })
.then(function() { document.querySelector('.spinner').style.display = 'none'; });
}
```
<hr>
</markdown></div>
<div id="disqus_thread"></div>
<script type="text/javascript">
var disqus_title = "Promise.mapSeries";
var disqus_shortname = "bluebirdjs";
var disqus_identifier = "disqus-id-promise.mapSeries";
(function() {
var dsq = document.createElement("script"); dsq.type = "text/javascript"; dsq.async = true;
dsq.src = "//" + disqus_shortname + ".disqus.com/embed.js";
(document.getElementsByTagName("head")[0] || document.getElementsByTagName("body")[0]).appendChild(dsq);
})();
</script>
<noscript>Please enable JavaScript to view the <a href="https://disqus.com/?ref_noscript" rel="nofollow">comments powered by Disqus.</a></noscript>
3 changes: 1 addition & 2 deletions docs/docs/new-in-bluebird-3.md
Expand Up @@ -28,7 +28,6 @@ All collection methods now support objects that implement [ES6's *iterable*](htt

[Promise.props](.) and [.props](.) now support [ES6 `Map`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map) objects along with normal objects. Actual `Map` objects are only considered for their entries in the map instead of both entries and properties.

[Promise.each](.) and [.each](.) don't return the original array anymore. They return a new array that contains returned values from the iterator callback function in respective positions to the old array, same as [Promise.map](.). The aliases `Promise.mapSeries` and `.mapSeries` have been added for these methods, respectively.

##Warnings

Expand All @@ -40,6 +39,7 @@ Warnings have been added to report usages which are very likely to be programmer
- Added [.suppressUnhandledRejections\(\)](.).
- Added [.catchThrow\(\)](.).
- Added [.catchReturn\(\)](.).
- Added [Promise.mapSeries\(\)](.) and [.mapSeries\(\)](.)


##Deprecations
Expand All @@ -57,6 +57,5 @@ Warnings have been added to report usages which are very likely to be programmer
- Cancellation redesign.
- Promise progression has been completely removed.
- [.spread](.)'s second argument has been removed.
- [Promise.each](.) and [.each](.) don't return the original array anymore. They return a new array that contains returned values from the iterator callback function in respective positions to the old array, same as [Promise.map](.).
- [.done](.) causes an irrecoverable fatal error in Node.js environments now. See [#471](.) for rationale.
- Errors created with [Promise.reject](.) or `reject` callback of [new Promise](.) are no longer marked as [OperationalError](.)s.
23 changes: 20 additions & 3 deletions src/each.js
@@ -1,12 +1,29 @@
"use strict";
module.exports = function(Promise, INTERNAL) {
var PromiseReduce = Promise.reduce;
var PromiseAll = Promise.all;

Promise.prototype.mapSeries = Promise.prototype.each = function (fn) {
function promiseAllThis() {
return PromiseAll(this);
}

function PromiseMapSeries(promises, fn) {
return PromiseReduce(promises, fn, INTERNAL, INTERNAL);
}

Promise.prototype.each = function (fn) {
return this.mapSeries(fn)
._then(promiseAllThis, undefined, undefined, this, undefined);
};

Promise.prototype.mapSeries = function (fn) {
return PromiseReduce(this, fn, INTERNAL, INTERNAL);
};

Promise.mapSeries = Promise.each = function (promises, fn) {
return PromiseReduce(promises, fn, INTERNAL, INTERNAL);
Promise.each = function (promises, fn) {
return PromiseMapSeries(promises, fn)
._then(promiseAllThis, undefined, undefined, promises, undefined);
};

Promise.mapSeries = PromiseMapSeries;
};
2 changes: 1 addition & 1 deletion test/mocha/each.js
Expand Up @@ -28,7 +28,7 @@ describe("Promise.each", function() {
it("should return the array's values mapped", function() {
var a = [promised(1), promised(2), promised(3)];
var b = [];
return Promise.resolve(a).each(function(val) {
return Promise.resolve(a).mapSeries(function(val) {
b.push(3-val);
return val + 2;
}).then(function(ret) {
Expand Down

0 comments on commit 526183e

Please sign in to comment.