New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
New .each behaviour and implicit returns #821
Comments
It was actually a very common request for The reason it is "surprising" for If you don't need the return value you can just not use it - did you have code that explicitly relied on |
Previously, Behaviour in Bluebird 2.x: > Promise.each(["a", "b", "c"], item => console.log(item) ).then(result => console.log(result) )
a
b
c
Promise {
_bitField: 0,
_fulfillmentHandler0: undefined,
_rejectionHandler0: undefined,
_progressHandler0: undefined,
_promise0: undefined,
_receiver0: undefined,
_settledValue: undefined,
_boundTo: undefined }
> [ 'a', 'b', 'c' ] When using implicit returns, it's very easy to return I can't see why one would request for |
This is intended and was one of the most requested features If you want to reuse the array, use .return |
This does not cover the side-effect case. Promise.try(=> {
return ["a", "b", "c"];
})
.each(item => console.log(item) )
.then(final => console.log(final) ) The Promise.try(=> {
return somethingThatResolvesToAnArray();
})
.each(item => console.log(item) )
.then(final => console.log(final) ) There is no way
Again: why would this behaviour be intended? What is there to gain from a |
Because serial map is very useful and can be used for side effects just by returning the original values as well, without having to implement 2 separate methods |
But this isn't a serial map. It's You could even add a |
|
Right. So why not add a |
Then you would have |
Having mutually exclusive options is not unheard of - in fact, it's made possible by each option being optional. An extra line of "you can't use these together" documentation (if that - if it doesn't make sense, people likely won't even try it) is certainly a better solution than unexpected behaviour and loss of functionality in every-day usage, in my opinion. |
I don't see how there is loss of functionality, what is essentially a useless return value (the same array you were passed) is replaced with a possibly useful one that you can discard if you have no use for it. |
The loss of functionality is the ability to cause side-effects with a I also just realized that the problem isn't even just with implicit returns - even if you return nothing, that will of course be treated as The only way to mitigate this, is to explicitly return the original value from the Promise.try(function() {
return ["a", "b", "c"];
}).each(function(item) {
console.log(item);
return item;
}).then(function(final) {
console.log(final);
}) ... which 1) defeats the point of it being an |
The way I see it is that it breaks current, existing code and also the learning curve. Users migrating to bluebird might not realize that |
This functionality is still there, when you use .each for side effects you don't care about the return value. Even |
+1 for not changing the existing functionality of .each |
But this change doesn't make it return undefined. It returns transformed values, which could be undefined, or could not be. You don't know, and if you guess wrong, you have an open failure. The functionality as I described is not 'still there'. You can no longer let the original array fall through. You need to manually work around it. |
my point is that it doesn't matter what it returns if you are not going to use the return value if you find the return value of the same array you already have useful, then that's a minor inconvenience, not some loss of functionality |
This is 1) the logical behaviour (especially given the heavily chained model of Bluebird), 2) the previous behaviour, and 3) the consistent behaviour (ie. equivalent of On the other hand, we now have a I see no reason not to change this. EDIT: It's also certainly not a 'minor inconvenience'. Bluebird is typically intertwined with your entire codebase, and side-effects in iteration are an extremely common scenario. |
Not sure about anyone else, but if I want to map, I use |
It doesn't matter what the return value is if you are not going to use it, if you use .each for side effects then you can do it regardless of what it returns. Returning the same thing you already have is almost by definition not functionality but pure convenience. |
As I've already explained several times, this is the logical and consistent behaviour, and an extremely common requirement for The point of |
This wasn't the point of .each at all. In fact, the return value was originally chosen because it felt trivially more useful than returning
Again, map is only for concurrent execution and managing concurrency. |
I am not just talking about Bluebird's
So why throw that away?
This isn't sequential execution. This is sequential mapping. That is something considerably different, and belongs in its own method. Whether that is As it stands,
It is not. Array.map (and pretty much every other |
I mostly agree with @joepie91 - I think we need to own up to the fact that we're going the utility grab-bag direction here and just provide |
I don't think separate tap-like each is even worth its own method if a mapSeries is available. |
It wouldn't have to be - that was already the old behaviour of |
In 2.x there was no .mapSeries that's why it's not the same situation. |
As a bystander having not really heard much about Bluebird before, reading this thread and petkaantonov's behaviour in it has solidified in my mind that the next time I go looking for a javascript library it will not be from here. |
@petkaantonov the name makes no sense, thats the problem. Its worth having another method in this case just for clarity. |
@spion ok, let's see what Benjamin says as well |
I'd vote for deprecating
Actually, it can - and I'd love that option. Make |
@bergus so what would be the return-input-array-while-executing-serially collection method if each is deprecated? |
Aside from not being in favour of deprecating EDIT: Unless you mean soft deprecation, that is. |
None. Weren't you arguing yourself that it was not needed? I'm all in for "Thou shalt not use side effects!" btw :-) Of course, we still can fill it in by Promise.prototype.each = function(cb) {
return this.mapSerial(cb).return(this);
} |
@bergus I couldn't agree more but this whole thread seems to be opposed to that. Also, deprecation is not even a breaking change while changing .each back to 2.x would be, so that's all good reasons for deprecating it. |
Semver makes it so that a breaking change (and resulting major version bump) is not a problem, though. Especially with one being released so quickly, the impact would be near non-existent. |
I'm for deprecating (not removing) In JS, you can That said I'm not the only user of this library - so if it is "popular enough" I think the current solution is pretty fine. |
👍 to @joepie91 for arguing. This was one of the only changes in 3.0 I didn't get at all. I would actually find it more reasonable if |
"Overloading" However, |
In 3.0, the behaviour of
each
was changed to behave like a sequentialmap
. I feel this was a mistake. The following code:... will result in
final
holding[undefined, undefined, undefined]
, which is not what one would expect for an iterator that is meant to be used for side-effects.This problem occurs not only with ES6 arrow functions, but also with implicit returns in other languages like CoffeeScript. I'd consider this a major footgun, as virtually no side-effect-y methods will return the original value.
Should this 'sequential map' behaviour not simply have moved to a new, separate method, without affecting the behaviour of
each
?The text was updated successfully, but these errors were encountered: