Unexpected `await` inside a loop. (no-await-in-loop) - javascript

How Should I await for bot.sendMessage() inside of loop?
Maybe I Need await Promise.all But I Don't Know How Should I add to bot.sendMessage()
Code:
const promise = query.exec();
promise.then(async (doc) => {
let count = 0;
for (const val of Object.values(doc)) {
++count;
await bot.sendMessage(msg.chat.id, `💬 ${count} and ${val.text}`, opts);
}
}).catch((err) => {
if (err) {
console.log(err);
}
});
Error:
[eslint] Unexpected `await` inside a loop. (no-await-in-loop)

If you need to send each message one-at-a-time, then what you have is fine, and according to the docs, you can just ignore the eslint error like this:
const promise = query.exec();
promise.then(async doc => {
/* eslint-disable no-await-in-loop */
for (const [index, val] of Object.values(doc).entries()) {
const count = index + 1;
await bot.sendMessage(msg.chat.id, `💬 ${count} and ${val.text}`, opts);
}
/* eslint-enable no-await-in-loop */
}).catch(err => {
console.log(err);
});
However, if there is no required order for sending the messages, you should do this instead to maximize performance and throughput:
const promise = query.exec();
promise.then(async doc => {
const promises = Object.values(doc).map((val, index) => {
const count = index + 1;
return bot.sendMessage(msg.chat.id, `💬 ${count} and ${val.text}`, opts);
});
await Promise.all(promises);
}).catch(err => {
console.log(err);
});

Performing await inside loops can be avoided once iterations doesn't have dependency in most cases, that's why eslint is warning it here
You can rewrite your code as:
const promise = query.exec();
promise.then(async (doc) => {
await Promise.all(Object.values(doc).map((val, idx) => bot.sendMessage(msg.chat.id, `💬 ${idx + 1} and ${val.text}`, opts);)
}).catch((err) => {
if (err) {
console.log(err);
}
});
If you still and to send one-after-one messages, your code is ok but eslint you keep throwing this error

I had a similar issue recently, I was getting lintting errors when trying to run an array of functions in a chain as apposed to asynchronously.
and this worked for me...
const myChainFunction = async (myArrayOfFunctions) => {
let result = Promise.resolve()
myArrayOfFunctions.forEach((myFunct) => {
result = result.then(() => myFunct()
})
return result
}

I am facing the same issue when I used await inside forEach loop. But I tried with recursive function call to iterate array.
const putDelay = (ms) =>
new Promise((resolve) => {
setTimeout(resolve, ms);
});
const myRecursiveFunction = async (events, index) => {
if (index < 0) {
return;
}
callAnotherFunction(events[index].activity, events[index].action, events[index].actionData);
await putDelay(1);
myRecursiveFunction(events, index - 1);
};

Related

How to push failed promises to separate array?

I am looping through ids that sends it to an async function and I want to return both success and failed data, right now I am only returning success data
const successContractSignature: LpContractSla[] = [];
for (const id of lpContractSlaIds) {
const data = await createLpSignature(context, id);
if (data) {
successContractSignature.push(data);
}
}
return successContractSignature;
If the createLpSignature throws error, do i need a try catch here? but wouldnt that exit the loop?
How can I push failed data to separate array without breaking the loop?
Unless there's a specific reason to avoid the call in parallel, it's always a good practice to avoid to start async calls in a for loop with await, since you are going to wait for each promise to resolve (or reject ) before executing the next one.
This is a better pattern which lets you also get all the results of the promises, either they resolved or rejected:
const successContractSignature: LpContractSla[] = await Promise.allSettled(lpContractSlaIds.map((id: string) => createLpSignature(context,id)))
return successContractSignature;
But if for some particular reason you need to make these calls in a sequence and not in parallel, you can wrap the single call in a try catch block ,that won't exit the loop:
for (const id of lpContractSlaIds) {
let data;
try {
data = await createLpSignature(context, id);
} catch(e) {
data = e
}
if (data) {
successContractSignature.push(data);
}
}
You can test it in this example:
const service = (id) =>
new Promise((res, rej) =>
setTimeout(
() => (id %2 === 0 ? res("ID: "+id) : rej('Error ID : '+id)),
1000
)
);
const ids = [1,2,3,4,5]
const testParallelService = async () => {
try {
const data = await Promise.allSettled(ids.map(id => service(id)))
return data.map(o => `${o.status}: ${o.reason ?? o.value}`)
} catch(e) {
console.log(e)
}
}
testParallelService().then(data => console.log("Parallel data: ", data))
const testSequentialService = async () => {
const res = [];
for (const id of ids) {
let data;
try {
data = await service(id);
} catch (e) {
data = e;
}
if (data) {
res.push(data);
}
}
return res;
};
testSequentialService().then((data) => console.log('Sequential Data: ', data));

Javascript Promise.all not running then

I've got a series of promises.
I never get the console log "Processed folders" printed out. Execution seems to stop once it hits the first await Promise.all call.
Not entirely sure where I've missed up?
const subfolders = [];
const exportFolder = () => {
// Other stuff happening here
const subfolder = {};
subfolder.items = [];
subfolder.items.push({ name: 'item 2.1' });
const folder = {};
folder.items = [];
folder.items.push({ name: 'item 1' });
folder.items.push({ name: 'item 2', isFolder: true, items: subfolder.items });
console.log('Folder:', folder);
console.log('Started');
exportFolderToCsv(folder).then(response => console.log('Finished', response));
};
const exportFolderToCsv = async folder => {
console.log('Processing folders');
let promises = [];
for (const folderItem of folder.items) {
if (folderItem.isFolder && folderItem.items.length > 0) {
subfolders.push(folderItem);
return;
}
promises.push(processFolderItem(folderItem));
}
await Promise.all(promises).then(response => console.log('Processed folders:', response));
if (subfolders.length > 0) {
console.log('Processing subfolders');
promises = [];
for (const folderItem of subfolders.items) {
promises.push(processFolderItem(folderItem));
}
await Promise.all(promises).then(response => console.log('Processed subfolders:', response));
}
console.log('Finished');
};
const processFolderItem = folderItem => new Promise(resolve => {
console.log('Processing folder item');
// To stuff here with folderItem, get Doc Chars, process row and resolve
getCharacters(folderItem)
.then(response => {
console.log('Processed folder item characters list:', response);
createCSVRow(folderItem, response)
.then(response => {
console.log('Processed CSV row:', response);
resolve(response);
})
});
});
const getCharacters = folderItem => new Promise(resolve => {
console.log('Processing folder item characters list');
// To stuff here with folderItem and then resolve
const characters = 'Foobar characters';
resolve(characters);
});
const createCSVRow = (folderItem, characters) => new Promise(resolve => {
console.log('Processing CSV row');
// To stuff here with folderItem and characters and then resolve
const csvRow = 'Foobar row';
resolve(csvRow);
});
exportFolder();
The function is returned before any Promise call is executed, it should be continue. So please resolve it first.
const exportFolderToCsv = async (folder) => {
// ...
for (const folderItem of folder.items) {
if (folderItem.isFolder && folderItem.items.length > 0) {
subfolders.push(folderItem)
return // here lies the problem, it should be `continue` instead
}
promises.push(processFolderItem(folderItem))
}
// ...
}
The Promise.all method, will catch errors if at least one Promise in chain throws an error or reject it.
It seems that one of your promises got an exception.
You can debug it simply adding the catch like so:
Promise.all(PromiseList).then(()=>console.log("all works done") ).catch(errors=>console.log("something wrong",errors))
If you are using await there is no point in chaining.
let response = await Promise.all(promises);
console.log('Processed subfolders', response)
Your promises will encounter errors, and you need to consider the failed situations.
At the same time, Promise.all will stop if either one promise failed, so maybe your Promise.all failed.
try {
Promise.all(promises).then(response => console.log('Processed subfolders', response));
} catch (err) {
console.log(err);
}
// or to check your promise result
const folderResult = await Promise.all(promises);
to check the exact result

How to fix Unexpected `await` inside a loop error - firebase function - [duplicate]

How Should I await for bot.sendMessage() inside of loop?
Maybe I Need await Promise.all But I Don't Know How Should I add to bot.sendMessage()
Code:
const promise = query.exec();
promise.then(async (doc) => {
let count = 0;
for (const val of Object.values(doc)) {
++count;
await bot.sendMessage(msg.chat.id, `💬 ${count} and ${val.text}`, opts);
}
}).catch((err) => {
if (err) {
console.log(err);
}
});
Error:
[eslint] Unexpected `await` inside a loop. (no-await-in-loop)
If you need to send each message one-at-a-time, then what you have is fine, and according to the docs, you can just ignore the eslint error like this:
const promise = query.exec();
promise.then(async doc => {
/* eslint-disable no-await-in-loop */
for (const [index, val] of Object.values(doc).entries()) {
const count = index + 1;
await bot.sendMessage(msg.chat.id, `💬 ${count} and ${val.text}`, opts);
}
/* eslint-enable no-await-in-loop */
}).catch(err => {
console.log(err);
});
However, if there is no required order for sending the messages, you should do this instead to maximize performance and throughput:
const promise = query.exec();
promise.then(async doc => {
const promises = Object.values(doc).map((val, index) => {
const count = index + 1;
return bot.sendMessage(msg.chat.id, `💬 ${count} and ${val.text}`, opts);
});
await Promise.all(promises);
}).catch(err => {
console.log(err);
});
Performing await inside loops can be avoided once iterations doesn't have dependency in most cases, that's why eslint is warning it here
You can rewrite your code as:
const promise = query.exec();
promise.then(async (doc) => {
await Promise.all(Object.values(doc).map((val, idx) => bot.sendMessage(msg.chat.id, `💬 ${idx + 1} and ${val.text}`, opts);)
}).catch((err) => {
if (err) {
console.log(err);
}
});
If you still and to send one-after-one messages, your code is ok but eslint you keep throwing this error
I had a similar issue recently, I was getting lintting errors when trying to run an array of functions in a chain as apposed to asynchronously.
and this worked for me...
const myChainFunction = async (myArrayOfFunctions) => {
let result = Promise.resolve()
myArrayOfFunctions.forEach((myFunct) => {
result = result.then(() => myFunct()
})
return result
}
I am facing the same issue when I used await inside forEach loop. But I tried with recursive function call to iterate array.
const putDelay = (ms) =>
new Promise((resolve) => {
setTimeout(resolve, ms);
});
const myRecursiveFunction = async (events, index) => {
if (index < 0) {
return;
}
callAnotherFunction(events[index].activity, events[index].action, events[index].actionData);
await putDelay(1);
myRecursiveFunction(events, index - 1);
};

writeFile does not wait for variable to be instantiated

I'm new to node.js and javascript in general but I am having issues understanding why the writeFile function is writing a blank file. I think the for loop should be a Promise but I am not sure how to write it.
const removeProviders = function () {
readline.question('Where is the file located? ', function(filePath) {
let providerArray = fs.readFileSync(filePath).toString().split('\r\n');
console.log(providerArray);
let importFile = '';
for (let providerId in providerArray) {
getProvider(providerArray[providerId]).then((response) => {
let providerInfo = response.data;
return providerInfo;
}).then((providerInfo) => {
let entry = createImportEntry(providerInfo);
importFile += "entry";
})
}
fs.writeFile('C:/path/to/file.txt', importFile);
You can collect all the promises from the for-loop into an Array and then pass them into Promise.all() which will resolve only after all of them resolved. If one of the promises are rejected, it will reject as well:
const promises = providerArray.map((item) => {
return getProvider(item)
.then((response) => {
let providerInfo = response.data;
return providerInfo;
})
.then((providerInfo) => {
let entry = createImportEntry(providerInfo);
importFile += "entry";
});
});
Promise.all(promises).then(() => {
fs.writeFile('C:/path/to/file.txt', importFile, err => {
if (err) {
console.error(err);
}
});
});
While doing this you could also get rid of the importFile variable and collect directly the results of your promises. Promise.all().then(results => {}) will then give you an array of all results. Thus no need for an updatable variable.
The below approach may be useful.
I don't know your getProvider return Promise. you can set promise in getProvider method
const getProviderValue = async function(providerArray) {
return new Promise((resolve, reject) {
let importFile = '';
for (let providerId in providerArray) {
await getProvider(providerArray[providerId]).then((response) => {
let providerInfo = response.data;
return providerInfo;
}).then((providerInfo) => {
let entry = createImportEntry(providerInfo);
importFile += "entry";
})
}
resolve(importFile)
})
}
const removeProviders = async function () {
readline.question('Where is the file located? ', function(filePath) {
let providerArray = fs.readFileSync(filePath).toString().split('\r\n');
console.log(providerArray);
let importFile = await getProviderValue(providerArray)
fs.writeFile('C:/path/to/file.txt', importFile);
})
}
Your for loop does not wait for the promises to resolve. A better way to approach this problem would be to use reduce.
providerArray.reduce(
(p, _, i) => {
p.then(_ => new Promise(resolve =>
getProvider(providerArray[providerId]).then((response) => {
let providerInfo = response.data;
return providerInfo;
}).then((providerInfo) => {
let entry = createImportEntry(providerInfo);
importFile += entry;
resolve();
}))
);
}
, Promise.resolve() );
You can also use Promise.all but the out data may not be in the order that you expect if you append to the importFile variable.

trouble with a while loop

Im trying to use a while loop with my util() function (its commented out at the bottom of the code). When I try to run the program, I am stuck in an endless loop where i dont get farther than where I'm console logging out "getProjects running"
const axios = require("axios");
const _ = require("lodash");
axios.defaults.headers.common["Private-Token"] = "iTookMyPrivateKeyOut";
const user = "yshuman1";
let projectArray = [];
let reposExist = true;
async function getProjects() {
console.log("getProjects running");
await axios
.get(`https://gitlab.com/api/v4/users/${user}/projects`)
.then(function(response) {
const arr = _.map(response.data, "id").forEach(repo => {
projectArray.push(repo);
});
console.log(projectArray);
});
}
function deleteRepo(projectArray) {
console.log("array size", projectArray.length);
const arr = _.map(projectArray).forEach(item => {
axios
.delete(`https://gitlab.com/api/v4/projects/${item}`)
.then(() => {
console.log("deleted project ID: ", item);
})
.catch(error => {
console.log(error);
});
});
}
function util() {
getProjects()
.then(() => {
if (projectArray.length == 0) {
reposExist = false;
}
if (projectArray.length < 20) {
console.log("array is less than 20");
reposExist = false;
}
deleteRepo(projectArray);
})
.catch(error => {
console.log(error);
});
}
// while (reposExist) {
// util();
// }
The while loop is synchronous, while everything in any .then (or promise await) will be asynchronous. The initial thread will never terminate. Your code will simply queue up unlimited calls of getProjects which will only console.log.
The simple solution would be to figure out how often you want to call util (once a second? once every 5 seconds?) and await a Promise that resolves after that amount of time on each iteration.
let reposExist = true;
function util() {
console.log('running');
}
const resolveAfter5 = () => new Promise(res => setTimeout(res, 5000));
(async () => {
while (reposExist) {
util();
await resolveAfter5();
}
})();

Categories