Skip to content
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

Use for of instead of forEach where possible #1845

Merged
merged 6 commits into from
Dec 16, 2020
Merged

Conversation

dapplion
Copy link
Contributor

@dapplion dapplion commented Dec 7, 2020

for of instead of forEach:

  • It works on any iterable
  • It supports all kinds of control flow in the loop body, like continue, break, return, yield and await.
  • on errors there's one less stack line
  • forEach can potentially hide problems such as unhandled promises
  • personally, I find it more readable (tho this is subjective)

@github-actions github-actions bot added Api scope-networking All issues related to networking, gossip, and libp2p. labels Dec 7, 2020
Copy link
Member

@mpetrunic mpetrunic left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

TBH I like forEach more:

  • it's easier to cast type of item being iterated
  • easier access to item index and update of array being iterated
  • I frequently confuse for-in and for-of (having hard time figuring what iterates over what)

@dapplion
Copy link
Contributor Author

dapplion commented Dec 8, 2020

TBH I like forEach more:

  • it's easier to cast type of item being iterated
  • easier access to item index and update of array being iterated
  • I frequently confuse for-in and for-of (having hard time figuring what iterates over what)

.forEach is great for the scenarios that you described. This PR only changes the cases where none of these conditions are met. Also for ... in should never be used, but use for ... of Object.keys()

@dapplion
Copy link
Contributor Author

dapplion commented Dec 8, 2020

By using for of I found to instances of not handleded promises. Tagging the previous author according to git blame

  1. @tuyennhv could you take a look at this one

    Before it was
sortedBlocks.forEach((block) => this.chain.receiveBlock(block, false));

Should it be awaited or not?

  1. @mpetrunic could you take a look at this one
validators.forEach((v) => v.start());

@mpetrunic
Copy link
Member

mpetrunic commented Dec 9, 2020

  1. @mpetrunic could you take a look at this one
validators.forEach((v) => v.start());

We don't really care about awaiting there. If we are gonna await, Promise.all should be better fit.

That said, it has nothing to do with for-of vs forEach. It's just that in forEach callback we didn't put async keyword (validators.forEach(async (v) => {v.start()});). And once you moved it into async scope it shows error. It wouldn't display error if for-of is executed in non-async context/method.

@dapplion
Copy link
Contributor Author

dapplion commented Dec 9, 2020

It wouldn't display error if for-of is executed in non-async context/method.

The error @typescript-eslint/no-floating-promises still shows in non-async context, you can try it

@codeclimate
Copy link

codeclimate bot commented Dec 9, 2020

Code Climate has analyzed commit 5597e5c and detected 6 issues on this pull request.

Here's the issue category breakdown:

Category Count
Complexity 6

View more on Code Climate.

@mpetrunic
Copy link
Member

It wouldn't display error if for-of is executed in non-async context/method.

The error @typescript-eslint/no-floating-promises still shows in non-async context, you can try it

add brackets in forEach :)

@dapplion
Copy link
Contributor Author

dapplion commented Dec 9, 2020

add brackets in forEach :)

Sure, but here we should compare the one-line syntax to be fair.

validators.forEach(v => v.start())
for (const v of validators) v.start()

The error will show up in the second and not in the first. In multiline, it will show up in both styles

@mpetrunic
Copy link
Member

add brackets in forEach :)

Sure, but here we should compare the one-line syntax to be fair.

validators.forEach(v => v.start())
for (const v of validators) v.start()

The error will show up in the second and not in the first. In multiline, it will show up in both styles

That's not equivalent.
Without brackets there is implicit return so in for-of that would be:

for (const v of validators) {
   (function() { return return v.start() })()
}

@dapplion
Copy link
Contributor Author

dapplion commented Dec 9, 2020

add brackets in forEach :)

Sure, but here we should compare the one-line syntax to be fair.

validators.forEach(v => v.start())
for (const v of validators) v.start()

The error will show up in the second and not in the first. In multiline, it will show up in both styles

That's not equivalent.
Without brackets there is implicit return so in for-of that would be:

for (const v of validators) {
   (function() { return return v.start() })()
}

Why would you want that in a for loop?

@mpetrunic
Copy link
Member

Why would you want that in a for loop?

You don't. You don't wan't that in forEach which is what we/I did there since void-ing promise wasn't working in previous eslint version when you didn't want to await promise.

@twoeths
Copy link
Contributor

twoeths commented Dec 9, 2020

By using for of I found to instances of not handleded promises. Tagging the previous author according to git blame

  1. @tuyennhv could you take a look at this one

    Before it was

sortedBlocks.forEach((block) => this.chain.receiveBlock(block, false));

Should it be awaited or not?

no we don't have to but it should also be ok to use await here too

@dapplion
Copy link
Contributor Author

dapplion commented Dec 9, 2020

no we don't have to but it should also be ok to use await here too

Would you mind taking a look yourself @tuyennhv? That function uses non straightforward Promise pattern that I don't understand

Copy link
Member

@wemeetagain wemeetagain left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you see if there is a noticeable performance cost to using for-of instead of forEach?

@dapplion
Copy link
Contributor Author

dapplion commented Dec 15, 2020

Can you see if there is a noticeable performance cost to using for-of instead of forEach?

This is a very popular question and different benchmarks show different results, where sometimes forEach wins others for ... of. I guess it depends on the platform or engine then? If performance is important somewhere (like SSZ bytes comparison), we should use the regular for loop which is faster and noticeable once you do very large numbers of iterations (+1e6)

@bajohn
Copy link

bajohn commented Dec 15, 2020

Can you see if there is a noticeable performance cost to using for-of instead of forEach?

This is a very popular question and different benchmarks show different results, where sometimes forEach wins others for ... of. I guess it depends on the platform or engine then? If performance is important somewhere (like SSZ bytes comparison), we should use the regular for loop which is faster and noticeable once you do very large numbers of iterations (+1e6)

I think the difference between the two methods would be insignificant compared to the async operation happening inside the loop, probably not the most important place to optimize!

@dapplion
Copy link
Contributor Author

dapplion commented Dec 15, 2020

I think the difference between the two methods would be insignificant compared to the async operation happening inside the loop, probably not the most important place to optimize!

Agree, the reasons for choosing between for of and forEach should be code readability, features and subjective preference

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
scope-networking All issues related to networking, gossip, and libp2p.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

5 participants