DEV Community

NekoAlt
NekoAlt

Posted on

Modern JS, when to use map or for?

My main languages are C and Python. I'm not well versed in JS, and I want to learn clean code development.

Recently, I made a helper function to run some .sql files in a folder. I will put a reduced example here:

const runFiles = async (files) => {
  for (const file of files){
    const query = fs.readFileSync(file, "utf-8");
    await client.query(query);
  }
}

Coming from imperative, and usually single-threaded languages, this seems like a natural approach to me. For each file, run query, wait, repeat.

But, I've got a suggestion that the code should be like this:

const runFiles = async (files) => {
 Promise.all(files.map(async (file) => {
    const query = fs.readFileSync(file, "utf-8");
    await client.query(query);
 });
}

So I want to ask, to some fellow JS/Node developers here, comparing both versions:

  • Is the second one clearer for you over the first one?
  • Should I use a functional approach more often?
  • Could this be faster? Does it runs in parallel?

Also, can you recommend me a book or resource to learn how to develop clean and modern js?

Top comments (4)

Collapse
 
vonheikemen profile image
Heiker • Edited

When to use .map? When you want to transform the items in the array. That's it. If you want to transform the array in any way, by removing an item or adding one, .map is not the ideal method. If you just want to loop it's perfectly fine to use a for...of loop.


That said. I think the second example runs faster because you don't wait for each query, they all run concurrently.

Collapse
 
altneko profile image
NekoAlt

Thanks for your feedback, Heiker!

I see now why the second code could run faster. Thinking in a functional way is something I should practice more.

Collapse
 
voracious profile image
David R. Myers

I think it's important to note that the last example is slightly different than the first in that the wrapping function runFiles will not actually wait for the promises to resolve. You could always prepend the Promise.all call with an await, but in this instance, I think you might not really need await at all. In my experience, await is just syntactic sugar for Promise chaining. It's only necessary if you need synchronous steps in your asynchronous function. Your example could just be written as this:

const runFiles = (files) => {
 return Promise.all(files.map((file) => {
    const query = fs.readFileSync(file, "utf-8");

    return client.query(query);
 });
}

This gives you some flexibility, because you aren't forcing any Promises to resolve before moving on to the next Promise. In a consuming function, you now have two options.

const someFunction = async (files) => {
  // wait for runFiles to resolve all promises
  await runFiles(files)

  // or just allow it to run in the background
  runFiles(files)
}
Collapse
 
altneko profile image
NekoAlt

Wow, many thanks for your reply!

This way of doing things is new to me, and you're opening my eyes to think outside the single threaded box.

I will definitely try this approach, since it's more flexible.