NodeJs wait until all calls in loop are finished - javascript

I have the following code in node (beginner), I want to call a function after all the code has been executed in the loop, I know that I have to use promises, but I don't know how to apply them in this situation. The code has been simplified. Many thanks.
const axios = require('axios').default;
axios.post('url', {
params: {}
}).then(response=>{
var objResponse = response.data
Object.keys(objResponse).forEach(function (key){
axios.post('url', {
params: {}
}).then(response=>{
var n=0;
getCode(n);
})
})
**finishFunction**()
})
function getCode(n) {
axios.post('url', {
params: {}
}).then(response=>{
if(response.data){
if (response.data.code) {
getData();
}else{
if (n < 10) {
n++;
getCode(n);
} else {
getTimeout();
}
}
}
})
}
function getTimeout() {
axios.post('url', {
params: {}
})
}
function getData() {
axios.post('url', {
params: {}
}).then(response=>{
console.log('finished one loop');
})
}

Best way to achieve what you want is to use async/await with a regular for-loop.
Here is an example of what I mean. You can adjust it to your needs:
async doSomeStuff() {
const results = []
const res1 = await axios.post('url', {
params: {}
})
// assuming res1 is an Array
for (let i=0; i < res1.length; i++) {
const result = await axios.post('url2', {
params: {}
})
results.push(result)
}
return results
}
You can also call other async functions inside the loop as you are doing.

You could use Promise all and map together, alongside async/await
async function myFunction(){
const result = await axios.post('url');
const keys = Object.keys(result.data);
const requests = keys.map((key, i) => axios.post('url')/*chain more if u need*/)
const allResults = await Promise.all(requests);
// All done run your function below
}

If you are happy for each item in the loop to be run at the same time, you can use an array of promises and wait for everything in the array to finish. Then you just have to add promises to your functions which will take an unknown amount of time to run. Something like this might get you started:
const axios = require('axios').default;
axios.post('url', {
params: {}
}).then(response=>{
var objResponse = response.data
var proms = [];
Object.keys(objResponse).forEach(function (key){
proms.push(
axios.post('url', {
params: {}
}).then(response=>{
var n=0;
return getCode(n);
})
)
})
var items = Promise.all(proms);
items.then(function (results) {
// **finishFunction**()
});
})
function getCode(n) {
return new Promise(function (resolve, reject) {
axios.post('url', {
params: {}
}).then(response=>{
if(response.data){
if (response.data.code) {
getData();
}else{
if (n < 10) {
n++;
getCode(n).then(data => {
return resolve(data)
});
} else {
getTimeout().then(data => {
return resolve(data)
});
}
}
}
})
})
}
function getTimeout() {
return new Promise(function (resolve, reject) {
axios.post('url', {
params: {}
})
return resolve()
})
}
function getData() {
return new Promise(function (resolve, reject) {
axios.post('url', {
params: {}
}).then(response=>{
console.log('finished one loop');
return resolve(response)
})
})
}

Related

How to return promise value JS

I'm trying to return a value obtained in a promise statement and I'm getting continuously Promise { undefined }, someone knows how to do it correctly?
When I render the .ejs file with the data it get's anything.
// /documents/vocabulary file
async function transformar() {
return new Promise(resolve => {
fs.readFile('src/documents/Vocabulary2.txt', 'utf8', function(error, data) {
if (data) {
var d = data.split('\n');
var a = []
for (let i=0; i<d.length; i++) {
a.push({
eng: d[i].split('-')[0].trim(),
esp: d[i].split('-')[1].trim()
})
}
a.sort(dynamicSort('eng'));
resolve(a);
}
});
});
}
//index.js file
const vocabulary = require('./documents/vocabulary');
const myFunction = async () => {
const data = await vocabulary.transformar();
return data;
}
const vocab = myFunction();
console.log(vocab)
app.use('/', function(req,res){
res.render(path.join(__dirname+'/Vocabulary.ejs'), {vocabulary_data: vocab});
});
Thanks for reading!
First of all, your transformar() method does not actually return a promise. Make sure you do that first:
function transformar() {
return new Promise((resolve, reject) => {
fs.readFile('src/documents/Vocabulary2.txt', 'utf8', function(error, data) {
if (data) {
var d = data.split('\n');
var a = []
for (let i=0; i<d.length; i++) {
a.push({
eng: d[i].split('-')[0].trim(),
esp: d[i].split('-')[1].trim()
})
}
a.sort(dynamicSort('eng'));
resolve(a);
} else {
reject();
}
});
});
}
Here are some suggestions:
You don't need to use async, since you already return a promise in the method now
All code paths should end up in reject() or resolve(), to avoid memory leaks (like what Thomas suggested in the comment below)
Now, back to the issue of getting data out of your promise: as there is no support for top-level await, so you need to either chain a .then() from the returned promise:
vocabulary.transformar().then((data) => console.log('data', data));
... or use await in an IIFE:
(async () => {
const data = await vocabulary.transformar();
console.log('data', data);
})();
If the Promise is being executed in a function, then you can simply use await:
const myFunction = async () => {
const data = await vocabulary.transformar();
console.log('data', data);
}
myFunction();
You should try this code :
async transformar() {
return new Promise((resolve, reject) => {
console.log('Sorting data');
fs.readFile('src/documents/Vocabulary2.txt', 'utf8', async (error, data) => {
if (data) {
//format data on array-dict
var d = data.split('\n');
var a = []
for (let i = 0; i < d.length; i++) {
a.push({
eng: d[i].split('-')[0].trim(),
esp: d[i].split('-')[1].trim()
})
}
//sort data
await a.sort(dynamicSort('eng'));
resolve(a);
}
});
})
}
var data;
vocabulary.transformar().then(result=>{
data = result;
console.log('data', data);
});
Your asynchronous function isn't returning anything. Also, the fs.readFile function doesn't return promises, it takes a callback. You should wrap it within a promise and resolve it with your final value.
function transformar() {
return new Promise(function (resolve, reject) {
fs.readFile(
'src/documents/Vocabulary2.txt',
'utf8',
function (error, data) {
if (data) {
//format data on array-dict
var d = data.split('\n');
var a = [];
for (let i = 0; i < d.length; i++) {
a.push({
eng: d[i].split('-')[0].trim(),
esp: d[i].split('-')[1].trim(),
});
}
//sort data
a.sort(dynamicSort('eng'));
resolve(a);
} else {
reject(new Error('No data was found.'));
}
}
);
});
}
transformar()
.then(function (data) {
console.log(data);
})
.catch(function (err) {
console.error(err);
});

JavaScript - replace setTimeout with async / await

First, I know this is a common question. I'm trying to get a handle on how to use async / await in place of setTimeouts, but all the examples I see online use a setTimeout to simulate the async. This throws me off when it's a set timeout that I'm trying to replace.
In the function below, I want this.filteredResultsto await the results of an API call before trying to filter those results and assign it to this.filteredResults.
getResults() {
let allResults= airtableQuery.getTable("Transfers"); // API call using imported 'getTable' function
console.log(allResults); // returns full array ▶[] although it's not available for filtering yet.
setTimeout(() => { // I want to replace this timeout
this.filteredResults = allResults.filter(
(result) => result.fields.User === "dev"
);
}, 250); // random ms that is roughly how long airtableQuery takes for the API call.
},
And the airtableQuery:
getTable(table) {
let recordsArr = [];
base(`${table}`)
.select({
maxRecords: 8000,
})
.eachPage(
function page(records, fetchNextPage) {
records.forEach((record) => {
recordsArr.push(record);
});
fetchNextPage();
},
function done(err) {
if (err) {
this.$toasted.error(err);
}
}
);
return recordsArr;
},
Please make the outer function an async function and then await the results before filtering them.
async function getResults() {
let allResults = await airtableQuery.getTable("Transfers");
this.filteredResults = allResults.filter(
(result) => result.fields.User === "dev"
);
},
Given that getTable() is not a Promise, await will not do anything. For that reason, we can make getTable() return a Promise which will resolve with recordsArr.
getTable(table) {
return new Promise((resolve, reject) => {
let recordsArr = [];
base(`${table}`)
.select({
maxRecords: 8000,
})
.eachPage(
function page(records, fetchNextPage) {
records.forEach((record) => {
recordsArr.push(record);
});
fetchNextPage();
},
function done(err) {
if (err) {
this.$toasted.error(err);
reject(err)
}else {
resolve(recordsArr)
}
}
);
})
}
Hope it helps.
i always likes primise,this my code show you
getTable(table) {
return new Promise((res, rej) => {
let recordsArr = [];
base(`${table}`)
.select({
maxRecords: 8000,
})
.eachPage(
function page(records, fetchNextPage) {
records.forEach((record) => {
recordsArr.push(record);
});
fetchNextPage();
res(recordsArr)
},
function done(err) {
if (err) {
this.$toasted.error(err);
rej(err)
}
}
);
})
}
getResults() {
airtableQuery.getTable("Transfers").then(res => {
let allResults = res
console.log(allResults);
this.filteredResults = allResults.filter(
(result) => result.fields.User === "dev"
);
});
}

async issues with js generator and promises not returning result

I'm having yet another async issue where I'm lost and have no idea where or how to fix it. Forgive my bad naming.
api call to twitch api and returns an array its results.
exports.batchPromiseWrapper = function(arr) {
const filteredMungedDataArr = [];
let promiseBatachArray = arr.map(vod_id => {
var url = `https://api.twitch.tv/kraken/videos/${vod_id.id}/markers`;
var params = { api_version: 5 };
return axios
.get(url, {
params: params,
headers: {
"Client-ID": "xxxxxxxxxxxxxxx"
}
})
.then(res => {
return res.data;
})
.catch(function(error) {
console.log(error);
});
});
return Promise.all(promiseBatachArray)
.then(markers => {
if (markers !== null) {
markers.map(markerObj => {
if (markerObj.markers.game_changes !== null) {
markerObj.markers.game_changes.forEach(gameName => {
if (gameName.label === "Fortnite") {
filteredMungedDataArr.push(markerObj);
}
});
}
});
return filteredMungedDataArr;
}
})
.catch(err => {
if (err.status === 500 || err.status === 404) {
console.log("error: ", err, err.message);
}
});
};
The data looks like this:
[[1,2,3,4,5],[1,2,3,4,5]], generator will yield and make a promise.all call of 5 before pausing 5sec and continuing to the next batch of 5.
exports.batchFetchingGeneratorWrapper = function(generator, batchArray) {
let evalNextValue = generator.next();
let delay = (v, t) => {
return new Promise(resolve => {
setTimeout(resolve.bind(null, v), t);
});
};
if (!evalNextValue.done) {
exports.batchPromiseWrapper(evalNextValue.value).then(data => {
let newBatchArray = batchArray;
if (data !== undefined) {
newBatchArray = batchArray.concat(data);
}
delay(5000).then(() => {
exports.batchFetchingGeneratorWrapper(generator, newBatchArray);
});
});
} else {
console.log("yay done!", batchArray);
return batchArray;
}
};
I'm able to console the results in batchArray from batchFetchingGeneratorWrapper, but I unable to act on it and I know it has something to do with async and how it has yet to be resolved.
promiseDataWrapper
.then(data => {
return gatherData.cleanUpVODData(data);
})
.then(data => {
function* batchFetching(batchArray) {
for (let i = 0; i < batchArray.length; i++) {
yield batchArray[i];
}
}
let batchArrResult = [];
let g = batchFetching(data);
new Promise((resolve, reject) => {
gatherData.batchFetchingGeneratorWrapper(g, batchArrResult);
if (g.done) { // i dont think this works
console.log("batchArrResult 1: ", batchArrResult);
resolve(batchArrResult);
}
}).then(result => console.log("asdfasdf", batchArrResult)); // empty array is returned
});
As far as I can tell, the problem lies chiefly in batchFetchingGeneratorWrapper().
It should be a matter of :
fixing delay()
making appropriate returns to make the recursion work
ensuring that the function returns Promise.
Almost undoubtedly (syntactically) simpler with async/await but here it is with old-fashioned thens :
exports.batchFetchingGeneratorWrapper = function(generator, batchArray) {
let evalNextValue = generator.next();
let delay = (t) => {
return new Promise(resolve => {
setTimeout(resolve, t);
});
};
if (!evalNextValue.done) {
return exports.batchPromiseWrapper(evalNextValue.value).then(data => {
return delay(5000).then(() => {
return exports.batchFetchingGeneratorWrapper(generator, batchArray.concat(data || []));
});
});
} else {
console.log("yay done!", batchArray);
return Promise.resolve(batchArray); // <<< promise wrapped to ensure that batchFetchingGeneratorWrapper() returns Promise
}
};
And chain the batchFetchingGeneratorWrapper() call appropriately :
promiseDataWrapper
.then(data => gatherData.cleanUpVODData(data))
.then(data => {
function* batchFetching(batchArray) {
for (let i = 0; i < batchArray.length; i++) {
yield batchArray[i];
}
}
return gatherData.batchFetchingGeneratorWrapper(batchFetching(data), []).then(batchArrResult => {
console.log('batchArrResult: ', batchArrResult);
return batchArrResult;
});
}).catch(error => {
console.log(error);
});

await http request response in constructor

I have a class, how I can create await for key value, that should be requested from http request in another method?
I don't know how to correct use await in this situation.
Here code, it returns only undefined:
class MyClass {
constructor(key = null) {
if (!!key)
this.key = key;
else
(async () => { this.key = await this.getKey(); })();
}
getKey(input) {
return new Promise((resolve, reject) => {
let options,
request,
data = '';
try {
options = {
host: '...',
port: '80',
path: '/',
method: 'GET',
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
}
};
request = http.request(options, (response) => {
response.setEncoding('utf8');
response.on('data', (chunk) => {
data += chunk.toString();
});
response.on('end', () => {
resolve(new RegExp('<div id="...".*>(.*)<\/div>', 'g').exec(data)[1]);
});
});
request.end();
} catch (error) {
reject(error);
}
});
}
}
a better usage would be to use await for the var you'll put in parameter :
let key = await getKey();
let myClass = new MyClass(key);
Perhaps I'm as confused about async await as you are, but it doesn't really seem necessary in this scenario.
class MyClass {
initKey() {
this.getKey().then(d => { this.key = d })
}
getKey(){
return new Promise((resolve, reject) => {
resolve("the promised value")
})
}
}
let t = new MyClass()
t.initKey()

Async / Await, how do I force my function to wait until promise is resolved before it continues?

I would like my function to execute until the nextPageToken is null. When I execute the function the first time, it waits for the promise to be resolved. However as soon there is a nextPageToken present on the response, the function does not wait for the response and a stack overflow occurs.
It seems that f() is not suspended on the when await p.then() is called.
Am I totally misunderstanding how async/await works?
Any help would be greatly appreciated...
public apiResult2(path: string, objectName: string, params: any = { }) {
let returnArray = [];
const p = new Promise<any> ((resolve, reject) => {
gapi.load('auth2:client', () => {
gapi.client.request({
path: path,
params: params
}).then(response => {
// resolve this promise with the first key in the response object.
resolve(response.result);
}, reason => {
console.log(reason);
reject(reason.result);
});
});
});
let f = async () => {
let nextPageToken = null;
do {
let r = await p.then(result => {
if (result.hasOwnProperty(objectName)) {
for (let obj of result[objectName]) {
returnArray.push(obj);
}
}
if (result.hasOwnProperty('nextPageToken')) {
params.nextPageToken = result.nextPageToken;
return result.nextPageToken;
// nextPageToken = result.nextPageToken;
} else {
params.nextPageToken = null;
return null;
// nextPageToken = null;
}
});
nextPageToken = r;
console.log(r);
} while (nextPageToken);
};
f();
return returnArray;
}
If your function needs to "await" some async call, then it must also be async. Your function apiResult2 is not going to wait for f to be finished, in order to return returnArray.
EDIT:
The main issue here is that you are trying to reuse the promise p to make different requests, but this is not possible. The promise p will be initialized with the parameters for the first request, and all the calls to p.then will be fulfilled with the same result: the response for the first page request.
I made some small changes to your code, and got it working with a mocked interface:
const apiResult2 = async (path: string, objectName: string, params: any = { }) => {
const requestPage = async () => new Promise<any> ((resolve, reject) => {
gapi.load('auth2:client', () => {
gapi.client.request({
path: path,
params: params
}).then(response => {
// resolve this promise with the first key in the response object.
resolve(response.result);
}, reason => {
console.log(reason);
reject(reason.result);
});
});
});
let returnArray: string[] = [];
do {
const page = await requestPage();
if (page.hasOwnProperty(objectName)) {
for (let obj of page[objectName]) {
returnArray.push(obj);
}
}
if (page.hasOwnProperty('nextPageToken')) {
params.nextPageToken = page.nextPageToken;
} else {
params.nextPageToken = null;
}
} while (params.nextPageToken);
return returnArray;
}
Usage example:
apiResult2(path, objectName, params).then(
result => console.log(result),
err => console.log('error', err)
);

Categories