In the following exported function which is from a Nextjs app as an API page, the domainnames array is returning nothing in the 200 response.
However, if I do not use the GetDomainStatus() function and just push items from response.data.results into domainnames, then the JSON response is filled.
export default function GetSuggestions(req, res){
const keyword = req.query.q;
const tlds = '.com,.net,.io,.org,.co,.xyz,.app,.us,.blog,.shop,.land,.video,.review,.host,.dev';
let queryPath = `${suggestionsURL}?include-registered=false&tlds=${tlds}&include-suggestion-type=true&sensitive-content-filter=true&use-numbers=true&max-length=20&lang=eng&max-results=100&name=${keyword}&use-idns=false`
let domainnames = [];
axios.get(queryPath).then(response => {
response.data.results.forEach(item => {
GetDomainStatus(item.name).then(a => {
domainnames.push({
name: item.name,
avail: a
})
})
})
res.status(200).json(domainnames);
});
}
is this a scope issue where I actually cannot access domainnames array from within the promise?
This solution worked. Not sure if its the best, but uses the promise.all solution.
const domainnames = [];
const promises = [];
axios.get(queryPath).then(response => {
response.data.results.forEach(item => {
let newPromise = GetDomainStatus(item.name).then(a => {
domainnames.push({
name: item.name,
avail: a
})
});
promises.push(newPromise);
})
Promise.all(promises).then(r => res.status(200).json(domainnames));
});
Related
I'm looking to assign a value to an array of objects inside a Axios call. I can't figure out how to assign the values to the new array. Below is the code I have so far.
Thank you.
app.get('/matchedusers', (req, res) => {
plexClient.getAllUsers().then(plexUsers => {
axios.get('https://tautulli.link/api/v2?apikey=API-KEY&cmd=get_users_table&length=150')
.then((tautulliUser) => {
let tautulliUsers = tautulliUser.data.response.data.data
let matchedUsers = []
let timedUsers = []
// Doing a check to see if tautulli user is also a plex user.
tautulliUsers.forEach((tautulliUser) => {
plexUsers.forEach((plexUser) => {
if(tautulliUser.username == plexUser.username || tautulliUser.username == plexUser.email){
matchedUsers.push({id: plexUser.id, username: plexUser.username, email: plexUser.email})
}
})
})
matchedUsers.forEach((user) => {
axios.get(`https://tautulli.link/api/v2?apikey=API-KEY&cmd=get_user_watch_time_stats&user_id=${user.user_id}&query_days=7`)
.then((watchTime) => {
// I want to add the matched user to timedUsers and add the watchTime response. Something like "timedUsers.push({...user, response: response})"
// This is the part that I can't figure out.
})
})
// Use the timedUsers array to display the results.
console.log(timedUsers)
res.json(timedUsers)
})
})
})
Use Promise.all(arrayOfPromises).then(...) to wait for an array of promises to finish.
If you ever introduce async code in a Javascript function, you always need to wait for the async response to finish, so you can only access the array inside the next .then().
Changing the entire function to use the async/await syntax can make this a little nicer to work with.
Promise.all(matchedUsers.map((user) =>
axios.get(`https://tautulli.link/api/v2?apikey=API-KEY&cmd=get_user_watch_time_stats&user_id=${user.user_id}&query_days=7`)
.then((response) => ({ ...user, response }))
).then(res => {
timedUsers = res;
}
Note the matchedUsers.map((user) => axios.get... syntax, which doesn't put a curly brace around the function body. This is an implicit Javascript return, so the map will return an array of the axios get promises.
Same for (response) => ({ ...user, response }), which is an explicit return of an object from a function. You must wrap the object in ( ) to signify it's an implicit object return.
You should use Promise.all, also I converted your route to async/await syntax as it is much easier to read and work with. It is not recommended to await in the loop it is better to push all the promises to an array and then use Promise.all to wait for the all.
app.get('/matchedusers', async(req, res) => {
const allUsers = await plexClient.getAllUsers();
const tautulliUser = await axios.get('https://tautulli.link/api/v2?apikey=API-KEY&cmd=get_users_table&length=150');
let tautulliUsers = tautulliUser.data.response.data.data
let matchedUsers = []
let timedUsers = []
tautulliUsers.forEach((tautulliUser) => {
plexUsers.forEach((plexUser) => {
if (tautulliUser.username == plexUser.username || tautulliUser.username == plexUser.email) {
matchedUsers.push({
id: plexUser.id,
username: plexUser.username,
email: plexUser.email
})
}
})
})
matchedUsers.forEach((user) => {
const response = axios.get(`https://tautulli.link/api/v2?apikey=API-KEY&cmd=get_user_watch_time_stats&user_id=${user.user_id}&query_days=7`);
timedUsers.push({ ...user,
response
});
})
const final = await Promise.all(timedUsers);
console.log(final)
res.json(final)
})
I suggest you use async/await syntax, and use Promise.all and map to resolve the request promises first then add them to timedUsers array.
app.get("/matchedusers", async (req, res) => {
const plexUsers = await plexClient.getAllUsers();
const tautulliUser = await axios.get(
"https://tautulli.link/api/v2?apikey=API-KEY&cmd=get_users_table&length=150"
);
let tautulliUsers = tautulliUser.data.response.data.data;
let matchedUsers = [];
let timedUsers = [];
// Doing a check to see if tautulli user is also a plex user.
tautulliUsers.forEach((tautulliUser) => {
plexUsers.forEach((plexUser) => {
if (
tautulliUser.username == plexUser.username ||
tautulliUser.username == plexUser.email
) {
matchedUsers.push({
id: plexUser.id,
username: plexUser.username,
email: plexUser.email,
});
}
});
});
// I want to add the matched user to timedUsers and add the watchTime response. Something like "timedUsers.push({...user, response: response})"
// This is the part that I can't figure out.
timedUsers = await Promise.all(
matchedUsers.map(async (user) => {
const response = await axios.get(
`https://tautulli.link/api/v2?apikey=API-KEY&cmd=get_user_watch_time_stats&user_id=${user.user_id}&query_days=7`
);
return { ...user, response }
})
);
// Use the timedUsers array to display the results.
console.log(timedUsers);
res.json(timedUsers);
});
I'm stuck on an issue where I'm parsing the results from an API in getSubscriptions(). This calls getUserSubs() which returns the following object:
When I call on subscriptions I get the expected array console (See "Works") snippet.
But when I try to iterate on the array subscriptions (See "Doesn't work"), then the contents of the function are not even called.
userSubs() is called for API data
async function getUserSubs(userId) {
const ref = collection(db, "users", userId, "subscriptions")
let subList = []
try {
const subIds = await getDocs(ref)
subIds.forEach(subRef => {
const docRef = subRef.data()
getDocData(docRef.product).then(product => {
subList.push(product)
})
})
return subList
} catch (e) {
console.error("Error in getting subscriptions: ", e)
return []
}
}
Works
function getSubscriptions(userId) {
getUserSubs(userId).then(subscriptions => {
console.log(subscriptions) // Works as intended
}
}
Doesn't work
function getSubscriptions(userId) {
getUserSubs(userId).then(subscriptions => {
subscriptions.forEach(x => {
console.log(x) // ISSUE: This isn't called
})
}
Also doesn't work
let a = []
getUserSubs(userId).then(subscriptions => {
subscriptions.forEach(x => a.push(x))
})
console.log(a)
I know there are similar questions asked but after reading them I'm still not able to resolve my issue.
Similar issues:
How to access the value of a promise?
Using async/await with a forEach loop
getUserSubs(userId).then(subscriptions => {
console.log(subscriptions) // Works as intended
}
No it doesn't. It only appears so because you are inspecting the live array that was mutated after it has been logged to the console.
Also doesn't work:
let a = []
getUserSubs(userId).then(subscriptions => {
subscriptions.forEach(x => a.push(x))
})
console.log(a)
Yes, for rather obvious reasons: the array is logged before you fill it. It would need to be either
getUserSubs(userId).then(subscriptions => {
let a = []
subscriptions.forEach(x => a.push(x))
console.log(a)
})
or
let a = []
const subscriptions = await getUserSubs(userId)
subscriptions.forEach(x => a.push(x))
console.log(a)
But none of these will solve your core problem: getUserSubs returns an empty array before it gets filled, in the lines
subIds.forEach(subRef => {
const docRef = subRef.data()
getDocData(docRef.product).then(product => {
subList.push(product)
})
})
return subList
you never wait for the getDocData promise. If you change that to
let subList = []
for (const subRef of subIds) {
const docRef = subRef.data()
const product = await getDocData(docRef.product)
subList.push(product)
}
return subList
or just
return Promise.all(subIds.map(subRef => {
const docRef = subRef.data()
return getDocData(docRef.product)
}))
it would work, as described in the question you already found.
(This might still not work. subIds looks suspiciously like a firebase snapshot, which is not an array and can neither be iterated nor does it have a .map method. In that case, you'll need to use forEach+push manually).
Honestly I wouldn't use the forEach() method. I think all you need to do to fix this is iterate over the results in a normal for loop.
for(let subscription of subscriptions) {
console.log(subscription);
}
OR
for(let index in subscriptions) {
console.log(subscriptions[index]);
}
If this doesn't do the trick, I'll open up a sandbox and look more in depth.
I have a get API call which looks like this
router.get('/review', async (req, res) => {
try {
const entity = await Entity.find();
const entityId = [];
Object.keys(entity).forEach((key) => {
entityId.push(entity[key]._id);
});
const results = [];
Object.keys(entityId).forEach(async (key) => {
const reviews = await Review.find({ entityId: entityId[key] });
results.push(reviews);
});
res.send(results);
} catch (e) {
res.status(500).send();
}
});
In entityId array it has a list of all the id i need and till there it works. Now what I want to do is iterate over each id of entityId and find the corresponding review that the entity has, push those review into a results array and return results.
review has an entityId field which is same as id of entity.
I also looked at - Using async/await with a forEach loop
which suggested to use for loop but it gave the following error.
iterators/generators require regenerator-runtime, which is too heavyweight for this guide to allow them. Separately, loops should be avoided in favor of array iterations.eslintno-restricted-syntax
How can I solve this?
forEach does not respect await therefore it may result in unintentional behaviour
you can use map to return array of promises from Review.find() and wrap those in await Promise.all({promises array here}) and use await on Promise.all.
This will result in parallel calls instead of doing them sequentially one after another.
const promisesWoAwait = this.entityIds.map(entityId => Review.find({ entityId }));
const result = await Promise.all(promisesWoAwait);
use promises instead of foreach.
Thy this
const data = async () => {
const entity = {
a: { _id: "1231" },
b: { _id: "1232" },
c: { _id: "1233" }
};
const entityId = [];
Object.keys(entity).forEach(key => {
entityId.push(entity[key]._id);
});
const promise = [];
Object.keys(entityId).forEach(async key => {
const reviews = Review({ entityId: entityId[key] });
promise.push(reviews);
});
const results = await Promise.all(promise);
};
const Review = (option) => {
return true;
};
data();
I know there are similar questions, but I can't find the answer.
First, please tell me if I'm doing something really wrong
I need to populate my state with data from an API call. This was working fine with code above:
export const GetPlanets = async () => {
const planets = await axios.get(`${BASE_URL}`).catch((e) => {
console.error(e);
})
return planets.data.results
}
But then I needed to make a second call to several links from one json response filed, and I managed to make it work (don't know if it is the correct approach, though)
const GetPlanets = async () => {
let planetas = {}
await axios.get(`${PLANETS_URL}`)
.then((p) => {
planetas = p.data.results
return axios.get(`${FILMS_URL}`)
}).then((f) => {
planetas.films.forEach((v, i) => {
planetas[i].film = f
})
})
})
.catch((e) => {
console.error(e);
})
return planetas
}
This is my component file, where I try to get the object, like I was doing before
useEffect(() => {
const fetchPlanetas = async () => { // ME TRYING...
const planetas = await GetPlanets()
setPlanetas(planetas)
setToShow(planetas[0])
};
fetchPlanetas()
}, [])
But all I get is undefined
You're getting an array of undefined because .map() needs a return value. In both your .map() callbacks, you are not returning anything.
const results = [1, 2, 3, 4]
const results2 = results.map(elem => {
elem = elem + 1
})
console.log(results2)
But, even if you did return something in your .map() callback, GetFilms(f) is asynchronous, so you would not get the results of GetFilms() mapped into the array as you would expect.
You have a couple of options:
If you have access to the API, send the films data along with the rest of the data when you do your first request.
Use async/await and Promise.all() to get responses.
I have an autocomplete input which, as the user types, fetches data from multiple endpoints, such as:
//service call to fetch data and return as single observable
getAutocompleteSuggestions() {
const subs$ = [
this.http.get(endpoint1),
this.http.get(endpoint2),
this.http.get(endpoint3)
];
return Observable.forkJoin(...subs$);
}
Each of these endpoints returns data of the form:
{ data: [], status: xyz }
I would like to use switchmap as I want to only show results from the final call, and have tried the following:
this.getAutocompleteSuggestions(query)
.switchMap(res => {
return res.data;
})
.subscribe((results: any) => {
this.results = results;
});
But the 'res' in the switchmap is an array, any idea how results can contain a single array containing the data from the response of any number of observables?
I don't fully understand what you want, but I think this is it:
$filter: Subject<string> = new Subject(); //I guess we have some value to filter by??
Push a value to the subject:
this.$filter.next(myNewValue);
In the constructor or init:
this.$filter
.switchMap(filterValue => { //Get the values when filter changes
subs$ = [
this.http.get(endpoint1 + filterValue),
this.http.get(endpoint2 + filterValue),
this.http.get(endpoint3 + filterValue)
];
return Observable.forkJoin(...subs$);
})
.map(results => { //now map you array which contains the results
let finalResult = [];
results.forEach(result => {
finalResult = finalResult.concat(result.data)
})
return final;
})
.subscribe(); //Do with it what you want
The entire steam will be executed again when we put a new value into our subject. SwitchMap will cancel all ready requests if there are any.
I have used something similar to this, and worked well so far:
let endpoint1 = this.http.get(endpoint1);
let endpoint2 = this.http.get(endpoint2);
let endpoint3 = this.http.get(endpoint3);
forkJoin([endpoint1, endpoint2, endpoint3])
.subscribe(
results => {
const endpoint1Result = results[0].map(data => data);
const endpoint2Result = results[1].map(data => data);
const endpoint3Result = results[2].map(data => data);
this.results = [...endpoint1Result, ...endpoint2Result, ...endpoint3Result];
},
error => {
console.error(error);
}
);
Obviously is a pretty simple example, you'll be able to handle better the results to suit your needs.