Javascript, Node.Js: Is Array.Foreach Asynchronous

JavaScript, Node.js: is Array.forEach asynchronous?

No, it is blocking. Have a look at the specification of the algorithm.

However a maybe easier to understand implementation is given on MDN:

if (!Array.prototype.forEach)
{
Array.prototype.forEach = function(fun /*, thisp */)
{
"use strict";

if (this === void 0 || this === null)
throw new TypeError();

var t = Object(this);
var len = t.length >>> 0;
if (typeof fun !== "function")
throw new TypeError();

var thisp = arguments[1];
for (var i = 0; i < len; i++)
{
if (i in t)
fun.call(thisp, t[i], i, t);
}
};
}

If you have to execute a lot of code for each element, you should consider to use a different approach:

function processArray(items, process) {
var todo = items.concat();

setTimeout(function() {
process(todo.shift());
if(todo.length > 0) {
setTimeout(arguments.callee, 25);
}
}, 25);
}

and then call it with:

processArray([many many elements], function () {lots of work to do});

This would be non-blocking then. The example is taken from High Performance JavaScript.

Another option might be web workers.

Is Array.forEach asynchronous?

forEach is synchronous. Your particular callback function, however, is not. So forEach synchronously calls your function, which starts its work, once for each entry in the array. Later, the work that started finishes asynchronously, long after forEach has returned.

The issue is that your callback is async, not that forEach is asynchronous.

In general, when you're using an API like forEach that doesn't do anything with the return value (or doesn't expect a promise as a return value), either:

  1. Don't pass it an async function, or

  2. Ensure that you handle errors within the function itself

Otherwise, you'll get unhandled errors if something goes wrong in the function.

Or of course:


  1. Use a try/catch block within the async function to catch and handle/report errors within the function itself.

Using async/await with a forEach loop

Sure the code does work, but I'm pretty sure it doesn't do what you expect it to do. It just fires off multiple asynchronous calls, but the printFiles function does immediately return after that.

Reading in sequence

If you want to read the files in sequence, you cannot use forEach indeed. Just use a modern for … of loop instead, in which await will work as expected:

async function printFiles () {
const files = await getFilePaths();

for (const file of files) {
const contents = await fs.readFile(file, 'utf8');
console.log(contents);
}
}

Reading in parallel

If you want to read the files in parallel, you cannot use forEach indeed. Each of the async callback function calls does return a promise, but you're throwing them away instead of awaiting them. Just use map instead, and you can await the array of promises that you'll get with Promise.all:

async function printFiles () {
const files = await getFilePaths();

await Promise.all(files.map(async (file) => {
const contents = await fs.readFile(file, 'utf8')
console.log(contents)
}));
}

Is Javascript forEach sync/async?

arr.forEach is not asynchronous. It is functionally very similar to a for loop, and does not return until the loop is over.

Your code

    timer.show('[0] res[' + lastindex + ']: '
+ (typeof res[lastindex] != 'undefined' ? res[lastindex] : 'NA'));

arr.forEach((item, i, arr) => {
res.push(item.reduce((a, b) => {
return a + b;
}));
// The element right before the last.
if (i == (lastindex - 1)) {
timer.show('[2] res[' + i + ']: ' + res[i]);
}
// The last element.
if (i == lastindex) {
timer.show('[3] res[' + i + ']: ' + res[i]);
}
});

// Peeping inside the last element before the loop ends!?
timer.show('[1] res[' + lastindex + ']: '
+ (typeof res[lastindex] != 'undefined' ? res[lastindex] : 'NA'));

Is roughly equal to

    timer.show('[0] res[' + lastindex + ']: '
+ (typeof res[lastindex] != 'undefined' ? res[lastindex] : 'NA'));

for (i = 0; i < arr.length; i++) {
item = arr[i];
res.push(item.reduce((a, b) => {
return a + b;
}));
// The element right before the last.
if (i == (lastindex - 1)) {
timer.show('[2] res[' + i + ']: ' + res[i]);
}
// The last element.
if (i == lastindex) {
timer.show('[3] res[' + i + ']: ' + res[i]);
}
}

// Peeping inside the last element before the loop ends!?
timer.show('[1] res[' + lastindex + ']: '
+ (typeof res[lastindex] != 'undefined' ? res[lastindex] : 'NA'));

which clearly should execute [2] and [3] before [1]

aynchronous forEach loop returning an array

Your first problem: The output array is empty because you use it before any of promises is executes. You have to await all promises before using the array.

The second problem: Promises can execute and therefore push items in (pseudo-)random order. Your output array can get shuffled.

The solution is (1) await all promises and (2) keep order of them (using Array.prototype.map):

async function foo(input) {
let output = await Promise.all(input.map(async element => {
return element * 2;
}));

return output;
}

// Call it
let input = [ 1, 2, 3, 4, ]; // This is your input array
foo(input).then(output => console.log(output));

The Promises.all is async function that takes array of promises and returns array of their results.

Array.prototype.map executes function for each item of the array.

More information:

  • tutorial and article about Promises on MDN

  • Array.prototype.map

await with array foreach containing async await

Use Array.prototype.map and Promise.all:

async function procesMultipleCandidates (data) {
let generatedResponse = []
await Promise.all(data.map(async (elem) => {
try {
// here candidate data is inserted into
let insertResponse = await insertionInCandidate(elem)
// and response need to be added into final response array
generatedResponse.push(insertResponse)
} catch (error) {
console.log('error'+ error);
}
}))
console.log('complete all') // gets loged first
return generatedResponse // return without waiting for process of
}

Or use a for/of loop if you don't want the loop run concurrently:

async function procesMultipleCandidates (data) {
let generatedResponse = []
for(let elem of data) {
try {
// here candidate data is inserted into
let insertResponse = await insertionInCandidate(elem)
// and response need to be added into final response array
generatedResponse.push(insertResponse)
} catch (error) {
console.log('error'+ error);
}
}
console.log('complete all') // gets loged first
return generatedResponse // return without waiting for process of
}

showing empty array in after pushing data from foreach into it in asynchronous function

The forEach function is not async-aware. You will need to use a simple for loop:

for( let i = 0; i < commandbody.length; i++ ) {
let arrayC = await commandsModel.getbyId(command);
cmdArray.push(arrayC);
}
console.log(cmdArray);

This should work if your outer function is marked as async too.



Related Topics



Leave a reply



Submit