Is it legit (or good practice) do a loop with a higher order function - like Array.map() - to perform some side effects ?
This is mostly a theoretical question since I notice that sometimes I found (or perform myself) some loops using the .map() method, like this:
let myObject = {...}
let myItemsArray = [ ... , ... ]
myItemsArray.map( (item, index) => {
// do some side effect for ex:
myObject.item[index] = item
}
But I know that this map() method actually returns an array. So calling myItemsArray.map() is like I'm returning an array without assign it to any variable.
So the question, is this legit? Should I avoid this and use a classic for() loop or a .forEach() method?
Side question: one of the reasons I'm asking this is because I need to perform some loops on an async function so promises and await operators are involved, I remember that forEach() loops are not ideal on an async function, but wanted to know why.
So the question, is this legit? Should I avoid this and use a classic for() loop or a .forEach() method?
If you aren't returning anything from the map function, then you should use forEach instead. You end up with the same result but you don't imply, to anyone maintaining your code, that you are returning something useful.
one of the reasons I'm asking this is because I need to perform some loops on an async function so promises and await operators are involved, I remember that forEach() loops are not ideal on an async function, but wanted to know why.
Neither forEach nor map will await if the function you pass to it returns a promise (which async functions do).
So there are three possible scenarios here:
// Your loop
...myItemsArray...
// Code that comes after the loop
...etc...
A: The items in the loop need to handled sequentially
Use a regular for () loop as the outer function won't pause.
B: The items in the loop can be handled in parallel and the code that comes after doesn't need to wait for it
Use a forEach.
C: The code that comes after needs to wait for everything in the loop to finish
Use a map. Pass the array of returned promises to Promise.all. Then await that.
Related
In the last year I've been using array methods like map and filter more often instead of the standard for loop on an array. It feels simpler to read and write, and does all the things I'm most likely going to do anyway, like create a local variable.
Often times I don't return anything though. Eslint doesn't like me very much though. According to them, they say you always need a return, otherwise its "probably a mistake"
https://eslint.org/docs/rules/array-callback-return
Why? Is just good practice? What are downsides of a return-less array method?
Been thinking on this for a while. Any insight or thoughts would be great.
Should I use array methods like map and filter, if I'm not going to return anything?
No, you should not.
Why? Is just good practice?
Yes. It is a good practice to use the appropriate iteration method for the type of iteration you are doing. There are numerous ways to iterate for a reason. Pick the appropriate mechanism.
What are downsides of a return-less array method?
Using .map() and .filter() without actually returning anything from the callback have the following downsides:
Your code is misleading. The point of .map() and .filter() is to iterate over the array and produce a new array. When a developer reads some code and sees .map() or .filter() being used, they expect that there should be a returned array. When they don't see it being done that way, they will be confused, will initially feel like they don't understand the code. If I were doing a code review on code like this, I would not approve of code like this.
Your code unnecessarily creates objects that are not used. That's just wasteful and is not a good practice. Instead, use an iteration method that does not produce an output array such as for/of, a regular for loop or .forEach().
Your code won't lint. Linters provide objections to things for a reason. Using .map() or .filter() without returning anything from the callback is, just as the linter says, "probably a programming mistake" because that is not how those functions are designed to be used and there are appropriate alternatives when you don't want a returned array.
So, if you're just trying to do an iteration without creating any resulting array, use for/of or .forEach() or some other iteration scheme that isn't specifically designed to create an output array that you don't want.
First you need to know about the difference between Map/Filter and forEach.
resuming.. forEach is mostly used when you want iterate an array with/as a procedure. check
Map and Filter are related to a callback function applied on every iteration.
The return statement of those is what is going to be evaluated, not the function By the Map/Filter function at the end. Reason why it's needed. Althought JS allows "whatever" And of course you are able to define that function what comes to our understanding as "The filter".
For Filter you can see that "true" and "false" as when the "data" is going to be filtered or not.
basically you can loop with map or forEach/for, the difference are the following:
foreach: This iterates over a list and applies some operation with side effects to each list member, this means that you are transforming THE CURRENT ARRAY you are looping.... or as noticed by #TiagoCoelho, you dont mess with the array at all, just loop thought it.
map: This iterates over a list, transforms each member of that list, and returns another list of the same size with the transformed members, this means that you will get a BRAND NEW ARRAY with the modified items and you will also have in memory your old array.
so basically it depends on what you want to do with the data inside of your array.
References
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/forEach https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/map
examples:
var a = [1, 2, 3, 4];
var b = [1, 2, 3, 4];
//multiply each item for 2, it will stay on the same array.
a.forEach(i => {
i = i * 2
})
//multiply the items of B for 2 but it will return a new array
var c = b.map(i => {
return i * 2
})
console.log(a); //modified reference
console.log(b); //stays the same
console.log(c); //new array
I know that iterators aren't really handled async with node and typescript, but the syntax sugar would seem to lie to me.
If I use a construct like this:
async function(checks) {
log('start loop')
checks.forEach(async (check) => {
actionResult = await this.runAction(check)
console.log('found 1 actionResult', actionResult)
}
})
log('end loop')
}
I would expect the async to apply to each inner iteration and wait on the loop to complete.
However the log output begs to differ:
start loop
end loop
found 1 actionResult {
So the inner event happens "after" the loop has run.
Is this correct/expected behavior? It seems a bit misleading.
I've seen some other syntax like:
Promise.all( elem => return someAsyncFn(x) )
which is another way to do it, but a bit hard to read.
for (const check of checks) {
A good old for (const elem of list) seems to properly observe the async of its outer wrapper, which is the way I usually end up going.
I also just found for...await of
So...
wondering if I have the syntax wrong in the first example? seems like it should work.
what is the most recommended way in 2020 to do this?
do generators or other techniques help?
related:
https://www.typescriptlang.org/docs/handbook/release-notes/typescript-2-3.html#async-iterators
what's the best method to handle asynchronous operations in an iterator with await?
None of the typical array iterations methods such as .forEach() and .map() are promise-aware. .map() can be used to collect an array of promises that you then await with Promise.all(), but that runs everything in parallel, not sequentially (useful sometimes, but it appears to not be what you were asking to do).
If you want to run the operations sequentially, then use a regular for loop instead of .forEach() since for will wait for your await and will properly sequence your asynchronous operations. This is the built-in, modern way to do it.
async function(checks) {
log('start loop')
for (let check of checks) {
actionResult = await this.runAction(check)
console.log('found 1 actionResult', actionResult)
}
log('end loop')
}
for await (loop parameters here) is when you have an actual asynchronous iterator which is not what you have here. Your situation is a regular iterator with an asynchronous operation inside it.
wondering if I have the syntax wrong in the first example? seems like it should work.
Your async callback you pass to .forEach() returns a promise, but .forEach() doesn't pay any attention to it. It just blindly goes right onto the next iteration of the loop (even though the asynchronous operations in the first iteration haven't finished yet). So, that's why it doesn't properly sequence things. .forEach() is not promise-aware. In fact, it finishes starting every single iteration of the loop before any of the asynchronous operations inside the loop have a chance to finish.
what is the most recommended way in 2020 to do this?
A regular for loop is the modern way to use await in a loop when you want the loop iterations to wait for the asynchronous operations before proceeding.
do generators or other techniques help?
Sometimes in some circumstances, but not needed here.
Avoid using forEach with promises as it's not promise aware
from MDN
forEach does not wait for promises. Kindly make sure you are aware of the implications while using promises(or async functions) as forEach callback.
The Promise.all approach is able to process the tasks in parallel (e.g. multiple network requests), while a loop is sequential. It might look uglier, but it can be beneficial for some tasks.
async function(checks) {
log('start loop');
await Promise.all(
checks.map((check) => this.runAction(check))
);
log('end loop');
}
So I have a bunch of mysql calls I'm making that I'm looking to make the code a little cleaner and more re-usable by separating the logic out into their own functions and what I want to do, since the number of calls can always grow, is build a promise array and execute them via Promise.all. I'm already familiar with this concept, but usually my approach would be something like this:
const promiseChain = []
array.forEach(item => promiseChain.push(new Promise((resolve, reject) { ... logic });
Or something similar to this. But I can't quit do it this way because I want to seperate out all the logic, so now I have a bunch of different functions, all returning promises. I'm a little confused on what syntax I can use to now just add the functions to the array without actually calling them. I did see one SO answer which specified something like this:
const array = [func1, func2]
Although I don't think this works, bc my functions would all have different parameters. Is there a good, clean way to add functions that return promises to an array (obviously without executing them) that also receive parameters? Or would it just be something like this:
promiseChain.push(new Promise((resolve, reject) => {
functionPromise(params).then(() => resolve()).catch(err => reject(err))
})
I guess what I'm asking is, is there a better way to do it then the above? Just seems like more code than necessary since those functions already return promises. Seems like overkill.
is there a better way to do it then the above?
Your example is equivelent to:
promiseChain.push(functionPromise(params));
Is there a good, clean way to add functions that return promises to an array (obviously without executing them) that also receive parameters
To add functions without executing them:
array.push(functionPromise);
The param can be bound to the functions in your array:
array.push(funcitonPromise.bind(funcitonPromise, ...params))
Then they can be invoked later by:
await Promise.all(array.map(func => func()));
I have one function that runs asynchronously, such as querying records from a database. The remaining code depends on the result, but runs synchronously. I am struggling to understand a good way to deal with this and would like to know the best practise?
Imagine the following code:
var usersDataPromise = getUsersDataFromDB();
var usersAgeArray = usersDataPromise.then(extractAges);
var averageAge = calculateAverageAge(usersAgeArray);
here, getUsersDataFromDB() runs synchronously and returns a promise. When the promise resolves, it runs extractAges(), a function that simply takes some of that data and returns an array. This function is sync. I want to pass that on to calculateAverageAge(), but this is where it breaks: calculateAverageAge does not wait for getUsersFromDB() to finish, and simply starts as soon as it can (usersAgeArray will be undefined).
What I tried:
I can make extractAges() return a promise instead of an array, and make calculateAverageAge wait with .then, by doing:
var usersDataPromise = getUsersDataFromDB();
var usersAgeArrayPromise = usersDataPromise.then(extractAges);
var averageAge = usersAgeArrayPromise.then(calculateAverageAge);
However, it feels as if I made the code needlessly complicated: I made extractAges() return a promise even though it runs asynchronously, not synchronously.
I realize I could have called calculateAverageAge() at the end of the extractAges function, but I'm trying to learn how to keep the code clean, and this feels as if I'm introducing side effects to a function that should only be extracting an array. Now the function description seems not fully accurate: it extracts an array but also calls on the next function.
I hope I've made this clear, I'm having trouble wrapping my head around it. What is the best way to deal with this? Thanks!
I can make extractAges() return a promise instead of an array
There's no reason to do that. Only because you are using a function in a then callback, it doesn't need to return promises.
and make calculateAverageAge wait with .then
Yes, you need to use then anyway, because the array is produced asynchronously and usersDataPromise.then(extractAges) always returns a promise. There's no alternative.
var averageAgePromise = getUsersDataFromDB().then(extractAges).then(calculateAverageAge);
What is the most performance effective foreach loop in JS with returning index?
best loading times have:
but how can I get index of each loop?
spacecrafts.forEach(function(spacecraft) {
// how can i call index without calling prototype and only with anonymous function?
console.log(index,spacecraft);
});
Like ShanShan said in the comment, the index is the second parameter of the anonymous function being passed to forEach. The code would look like this.
spacecrafts.forEach(function(spacecraft, index) {
console.log(index,spacecraft);
});
If for some reason you really need performance, go ahead and use BenchmarkJs to test your particular use cases and find what is fastest. In general, the built-in Array prototype functions aren't as performant as a basic for-loop. You might want to look into using a library like lodash, or underscore which say they try and be performant.