How to use foreach and promise - javascript

I need to get datas with nested foreach, but I can't fill my array.
At the end of this code I would like to have an array (segId) with my datas but it is empty (because of aynschronous).
I read that I had to use Promise.all but I can't beacause my promise are nested
I'm beginner so my code is far from perfect
How can I do that ?
async function getActivities(strava, accessToken)
{
const payload = await strava.athlete.listActivities({'access_token':accessToken, 'after':'1595281514', 'per_page':'10'})
return payload;
}
async function getActivity(strava, accessToken, id)
{
const payload = await strava.activities.get({'access_token':accessToken, 'id':id, 'include_all_efforts':'true'})
return payload;
}
async function getSegment(strava, accessToken, id)
{
const payload = await strava.segments.get({'access_token':accessToken,'id':id})
return payload
}
var tableau = []
var segId = []
const activities = getActivities(strava, accessToken)
activities.then(value => {
value.forEach((element, index) => {
const activity = getActivity(strava, accessToken, element['id'])
activity.then(value => {
value['segment_efforts'].forEach((element, index) => {
const segment = getSegment(strava, accessToken, element['segment']['id'])
segment.then(value => {
segId.push(value['id'])
})
//console.log(segId)
});
});
})
}) console.log(segId)
Regards
PS : Sorry for my english ...

Something like this should work. You need to always return the inner promises to include them in your promise chain. Consider splitting the code into functions to make it more readable.
getActivities(strava, accessToken).then(activities => {
return Promise.all(activities.map(elem => {
return getActivity(strava, accessToken, elem['id']).then(activity => {
return Promise.all(activity['segment_efforts'].map(elem => {
return getSegment(strava, accessToken, elem['segment']['id']).then(segment => {
segId.push(segment['id']);
});
}));
})
}));
})
.then(_ => {
console.log(segId);
});

Related

How can I convert this async function to promise?

Hello I have this async function which retrieves user profile and repositories through github api and returns them in an object.
And I want to convert this to a promise based function by using promise chaining (without any helper methods).
async function getUser(user) {
const profileResponse = await fetch(`https://api.github.com/users/${user}`);
const profileData = await profileResponse.json();
const repoResponse = await fetch(`https://api.github.com/users/${user}/repos`);
const reposData = await repoResponse.json();
// returning an object with profile and data
return {
profile:profileData,
repos:repoData,
};
}
//Calling the function here.
getUser("abufattah").then((res) => console.log(res));
I have managed to get it done using two helper functions and promise.all() method.
But how can I achieve the same thing by using promise chaining without any helper functions.
//Helper function 1: returns a promise with user profile data.
function getUserProfile(user) {
return fetch(`https://api.github.com/users/${user}`)
.then((res) =>res.json());
}
//Helper function 2: returns a promise with user repositories data.
function getUserRepos(user) {
return fetch(`https://api.github.com/users/${user}/repos?per_page=5&sort=created`)
.then((res) => res.json());
}
//Main function
function getUserWithPromise(user) {
return new Promise((resolve) => {
let profile = getUserProfile(user);
let repos = getUserRepos(user);
Promise.all([profile, repos]).then((values) => {
resolve({ profile: values[0], repos: values[1] });
});
});
}
// calling the function here
getUserWithPromise("abufattah").then((res) => console.log(res));
Transformation of async/await syntax to .then() calls is quite mechanical, especially if it doesn't involve any control flow syntax (loops or conditionals):
function getUser(user) {
return fetch(`https://api.github.com/users/${user}`).then(profileResponse => {
return profileResponse.json().then(profileData => {
return fetch(`https://api.github.com/users/${user}/repos`).then(repoResponse => {
return repoResponse.json().then(reposData => {
// returning an object with profile and data
return {
profile:profileData,
repos:repoData,
};
});
});
});
});
}
But there's no good reason to write code like that. If it's just that your target environment does not support async/await, let a transpiler do the transformation.
You can:
//Main function
function getUserWithPromise(user) {
return Promise.all([
fetch(`https://api.github.com/users/${user}`).then((res) =>res.json()),
fetch(`https://api.github.com/users/${user}/repos?per_page=5&sort=created`).then((res) => res.json())
]).then(([result1, result2]) => ({ profile: result1, repos: result2 }));
}
// calling the function here
getUserWithPromise("abufattah").then((res) => console.log(res));
Chain:
function getUserWithPromise(user) {
return fetch(`https://api.github.com/users/${user}`)
.then((res) => {
return fetch(`https://api.github.com/users/${user}/repos?per_page=5&sort=created`).then((fetch2Result) => ([res.json(), fetch2Result.json()]))
}).then(([result1, result2]) => ({ profile: result1, repos: result2 }))
}
// calling the function here
getUserWithPromise("abufattah").then((res) => console.log(res));

Async module returning promise [object Promise]

I am trying to export the value with instrument variable. however data is returning as [object Promise] than object. How can I assign module variable with the final result rather than the promise object.
var instruments = {
data: async () => {
return new Promise((resolve, reject) => {
/// Respond after retrieving the data
resolve({result : "...." }
);
}
}
var symbols = async () => {
const res = await instruments.data();
return res;
}
module.exports.instrument = symbols().then((data) => {
console.log('data');
return data;
}).catch((e) => {
console.log('error');
return {}
});
It looks like you want a singleton cache. Here is a basic implementation
cache.js
let data = {}
module.exports = {
getData: () => {
return data
},
setData: newData => {
data = newData
return
},
}
No need for async here. I would separate this code with the code that retrieves data.
fetchData.js
const cache = require('./cache')
const fetchData = () => {} // fetch data code here
fetchData().then(data => {
cache.setData(data)
})
try this
var instruments = {
data: () => {
return new Promise((resolve, reject) => {
/// Respond after retrieving the data
resolve({result : "...." });
}
}
var symbols = async () => {
const res = await instruments.data();
return res;
}
module.exports.instrument = symbols;
then import instrument method to call and then call
const instrument = require("./filepath");
instrument().then((data) => {
console.log('data');
}).catch((e) => {
console.log(e);
});
If your async function instruments.data() called, it'll await return Promise.
just append await at return for your expected result.
var instruments = {
data: async () => {
return await new Promise((resolve, reject) => {
// Respond after retrieving the data
resolve({result : "...." });
}
}
or remove async. it's same as above.
var instruments = {
data: () => {
return new Promise((resolve, reject) => {
// Respond after retrieving the data
resolve({result : "...." });
}
}

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.

How to use Fetch queries in a loop?

I make a request to the server via a map with different urls, then I set the data in State and use it for output. I want the requests to be consecutive but sometimes they do not work correctly and get bugs, how to write the code for normal data retrieval?
const urlList = ["countries", "states", "cities", "users"];
componentDidMount() {
urlList.map( (url, index) => {
return servicesAPI.getResourse(url).then( (body) => {
index !== 3 ? this.setState({
dataAPI : [...this.state.dataAPI, body] }) :
this.setState({
dataAPI : [...this.state.dataAPI, body],
loaded: true
})
})
})
export default class ServicesAPI {
_apiBase = `http://localhost:3001/`;
async getResourse(url) {
const res = await fetch(`${this._apiBase}${url}`);
if (!res.ok) {
throw new Error(`Could not fetch ${url}` +
`, received ${res.status}`)
}
return await res.json();
}
Use of Promise.all();
componentDidMount() {
const fetchPromises = [];
urlList.forEach( (url, index) => {
fetchPromises.push(servicesAPI.getResourse(url));
});
const allResourcesPromise = Promise.all(fetchPromises);
allResourcesPromise.then(data => {
// list with responses
}).catch(err => {
console.log(err.toString());
});
}
Sample example:
https://jsbin.com/vevufumano/1/edit?html,js,console,output
Also instead of then, where is possible, you can use async/await for more cleaner code.

Node.js Promise with mongoose

I have difficulty using Promise.
I want to get data from one more tables in mongodb.
but I fell in callback hell, So I tried to solve this but I couldn't.
What should I do? the result came out 'undefined'.
Many Thanks,
let mongoose = require('mongoose');
mongoose.Promise = global.Promise;
....
exports.Recommend = (id) => {
User.find({User_id: myId})
.then((result) => {
return Promise.resolve(result[0].age)
}).then(age => {
return new Promise((resolve,rejject)=>{
resolve(User.find()
.select('User_id')
.where('age').equals(age))
})
}).then((Users_id) => {
Users_id.forEach((user, idx, arr) => {
Count.find()
.select('board_id')
.where('User_id').equals(user.User_id)
.exec((err, items) => {
return new Promise((resolve,reject)=>{
resolve(
items.forEach((post, idx, arr) => {
posts.push(post.board_id)
}))
})
})
})
}).then(()=>{
console.log("posts:"+posts);
})
}
Avoid Promise.resolve, avoid using the new Promise constructor like Promise.resolve, avoid the Promise constructor antipattern, and avoid forEach, and don't forget to return the promise chain from your function:
exports.Recommend = (id) => {
return User.find({User_id: myId}).then(result => {
return User.find()
.select('User_id')
.where('age')
.equals(result[0].age));
}).then(user_ids => {
return Promise.all(user_ids.map((user, idx, arr) => {
return Count.find()
.select('board_id')
.where('User_id').equals(user.User_id)
.exec()
.then(posts => posts.map(post => post.board_id));
}));
}).then(board_ids => {
console.log("posts:"+board_ids);
})
}
You have the problem with 3rd .then, I would like to recommend you to use Promise.all function to run the parallel database query. Following example may help you
exports.Recommend = (id) => {
User.find({
User_id: myId
})
.then((result) => {
return User.find()
.select('User_id')
.where('age').equals(result[0].age)
}).then((Users_id) => {
return Promise.all(Users_id.map((user, idx, arr) => {
return Count.find()
.select('board_id')
.where('User_id').equals(user.User_id)
}));
}).then((Users_id) => {
Users_id.forEach(items => {
items.forEach(post => {
posts.push(post.board_id)
})
})
}).then(() => {
console.log("posts:" + posts);
})
}

Categories