How to refactor for-loop async/await with Promise.all()? - javascript

I'm trying to wrap my head around how to use Promise.all() in this code. I've read on articles that you can run async operations in parallel with Promise.all() to optimize for speed. Here's the current code in nested for-loops (bad):
type ListGroup = {
listId: string
groupIds: Array<string>
}
const listsAndGroups: Array<ListGroup> = []; // <-- put everything here
const { lists } = await mailchimp.get('/lists');
for (const list of lists) {
const listObj = { listId: list.id };
const { categories } = await mailchimp.get(
`/lists/${list.id}/interest-categories`,
);
for (const category of categories) {
const { interests } = await mailchimp.get(
`/lists/${list.id}/interest-categories/${category.id}/interests`,
);
Object.defineProperty(listObj, 'groupIds', {
value: interests.map((interest) => interest.id),
enumerable: true,
});
}
listsAndGroups.push(listObj);
}
Here's how I'm doing so far, I think I'm just running blindly here without really knowing what I'm doing:
const listsAndGroups: Array<ListGroup> = await getListsGroups(); // <-- put everything here
const getListsGroups = async () => {
const { lists } = await mailchimp.get('/lists');
const listGroups = lists.map((list) =>
getCategories(list.id).then((groups) =>
groups.map((group: Record<'groupIds', string>) => {
return {
listId: list.id,
...group,
};
}),
),
);
return Promise.all(listGroups);
};
const getCategories = async (listId: string) => {
const { categories } = await mailchimp.get(
`/lists/${listId}/interest-categories`,
);
const groups = categories.map((category) =>
getInterests(listId, category.id),
);
return Promise.all(groups);
};
const getInterests = async (listId: string, categoryId: string) => {
const { interests } = await mailchimp.get(
`/lists/${listId}/interest-categories/${categoryId}/interests`,
);
return { groupIds: interests.map((interest) => interest.id) };
};

You could simplify your operation many way, Here is one:
type ListGroup = {
listId: string
groupIds: Array<string>
}
const listsAndGroups: Array<ListGroup> = []; // <-- put everything here
const { lists } = await mailchimp.get('/lists');
const pandingLists = lists.map(list =>
mailchimp.get(`/lists/${list.id}/interest-categories`)
.then(data => [data, { listId: list.id }])
);
for (const [{ categories }, listObj] of await Promise.all(pandingLists)) {
const batch = categories.map(({ id }) =>
mailchimp.get(`/lists/${listObj.listId}/interest-categories/${id}/interests`).then(interests => {
Object.defineProperty(listObj, 'groupIds', {
value: interests.map(({ id }) => id),
enumerable: true,
});
}));
await Promise.all(batch).then(() => listsAndGroups.push(listObj));
}

Related

How to fetch data from the Jikanapi

I want to call an API here
https://api.jikan.moe/v4/top/anime to get the data in raw format and then create an array of only useful data out of it. What is the reason the data is not being consoled
const initialAnime = {
anime: [],
genresLoaded: false,
genres: [],
};
function createAnimeFromRawData(rawData, animeArray) {
const data = rawData.data;
data.forEach((animeData) => {
const anime = {
mal_id: animeData.mal_id,
title: animeData.title,
title_english: animeData.title_english,
type: animeData.type,
episodes: animeData.episodes,
status: animeData.status,
duration: animeData.duration,
rating: animeData.rating,
rank: animeData.rank,
synopsis: animeData.synopsis,
};
console.log(animeArray);
animeArray.push(anime);
});
}
const RawdataAnime = async (api, genre, paging) => {
const Animearray = [];
for (let i = 1; Animearray.length < 60 && i < 10; i++) {
const {
data: { results },
} = await axios.get(`${api}`);
createAnimeFromRawData(results, Animearray);
}
return Animearray;
};
export const fetchAnime = createAsyncThunk(
"myanimelist/topAnime",
async (thunkAPI) => {
const {
myanimelist: { genres },
} = thunkAPI.getState();
return RawdataAnime(`https://api.jikan.moe/v4/top/anime`, genres, false);
}
);
const animeSlice = createSlice({
name: "Myanimelist",
initialState: initialAnime,
extraReducers: (builder) => {
builder.addCase(getGenresAnime.fulfilled, (state, action) => {
state.genres = action.payload;
state.genresLoaded = true;
});
builder.addCase(fetchAnime.fulfilled, (state, action) => {
state.anime = action.payload;
});
},
});
export const store = configureStore({
reducer: {
netflix: netflixSlice.reducer,
anime: animeSlice.reducer,
},
});
I tried the code above to get an array of only useful parts of data in the code but there was nothing in the console. There was no error and no output.
Whereas the response.data will be something similar to the json below::
{
"pagination":{
...
},
"data":[
...
],
"links":{
...
},
"meta":{
...
}
}
I believe the error is in the snippet
const { data: { results }} = await axios.get(`${api}`); // There are no results in the returned content
createAnimeFromRawData(results, Animearray);
Try something like
const { data } = await axios.get(`${api}`); // Equivalent to response.data
const results = data?.data || []
createAnimeFromRawData(results, Animearray);

When I log Array there's an object inside, but when I'm trying to access that object it returns me undefined

This is my cache "component":
// imports
const useCache = (cacheName: string, url: string) => {
const cacheArray: Array<Object> = []
const getAllCaches = async () => {
const cacheNames = await caches.keys();
for (const cname of cacheNames) {
const cacheStorage = await caches.open(cname);
const cachedResponse = await cacheStorage.match(url);
const cdata = await cachedResponse?.json()
cacheArray.push({name: cname, data: cdata})
}
}
useEffect(() => {
getAllCaches()
.catch(err => console.log(err))
}, [])
const addCache = (response: any) => {
const data = new Response(JSON.stringify(response));
if ('caches' in window) {
caches.open(cacheName).then((cache) => {
cache.put(url, data);
});
}
const finalData = {name: cacheName, data: response}
cacheArray.push(finalData)
return data
}
const getCache = (cacheName?: string) => {
if (cacheName) {
return cacheArray.filter((i: any) => i.name === cacheName)[0]
}
else {
return cacheArray
}
}
const removeCache = (cacheName: string) => {
caches.delete(cacheName).then(function (res) {
return res;
});
}
return [
getCache as (cacheName?: any) => any,
addCache as (response: any) => any,
removeCache as (cacheName: any) => any
]
};
export default useCache;
Now here's code in my home component:
const [getCache, addCache, removeCache] = useCache("user", "http://localhost:3000")
useEffect(() => {
console.log(getCache())
console.log(getCache()[0])
console.log(getCache().length)
// the rest of code, not matter
and when I run home component (with vite and preact) it logging me Array, then unfedinfed, then 0 (but second should return object, and third should return 1) also I attached a screen from console.
Why it's returning me undefined and 0 length when it should return object and 1?
I'm using preact, vite, newest nodejs, typescript

Run async/await function inside a reduce Javascript [duplicate]

This question already has answers here:
JavaScript array .reduce with async/await
(11 answers)
Closed 6 months ago.
I need to fetch values from another API using the guid inside this particular array, then group them together (hence I used reduce Javascript in this case)
However, I could not get those values sumEstimatedHours and sumWorkedHours as expected. Can someone suggest a method please?
export const groupProjectsByPM = (listOfProjects) => {
const dir = "./json";
const estimatedHours = fs.existsSync(dir)
? JSON.parse(fs.readFileSync("./json/phases.json", "utf-8"))
: null;
let sumWorkedHours, sumEstimatedHours;
const groupedProjects = listOfProjects?.reduce(
(
group,
{
guid,
projectOwner: { name: POName },
name,
customer: { name: customerName },
deadline,
calculatedCompletionPercentage,
}
) => {
listOfProjects.map(async (element, index) => {
// const element = listOfProjects[index];
sumWorkedHours = await getWorkhoursByProject(element?.guid).then(
(res) => {
return res.reduce((acc, cur) => {
return acc + cur.quantity;
}, 0);
}
);
const filteredEstimatedHours = estimatedHours.filter(
(item) => item.project.guid === element.guid
);
sumEstimatedHours = filteredEstimatedHours.reduce((acc, cur) => {
return acc + cur.workHoursEstimate;
}, 0);
group[POName] = group[POName] || [];
group[POName].push({
guid,
name,
POName,
customerName,
deadline,
calculatedCompletionPercentage,
sumEstimatedHours,
sumWorkedHours,
});
return group;
});
return group;
},
[]
);
return groupedProjects;
};
here is an example of async/await inside reduce:
let's assume that we have an array of numbers
const arrayOfNumbers = [2,4,5,7,6,1];
We are going to sum them using reduce function:
const sumReducer = async () => {
const sum = await arrayOfNumbers.reduce(async (promisedSum, num) => {
const sumAcc = await promisedSum
// any promised function can be called here..
return sumAcc + num
}, 0)
console.log(sum)
}
So the trick is to remember to await the accumulator inside the reduce function
export const groupProjectsByPM = async (listOfProjects) => {
const dir = "./json";
const estimatedHours = fs.existsSync(dir)
? JSON.parse(fs.readFileSync("./json/phases.json", "utf-8"))
: null;
let sumWorkedHours, sumEstimatedHours;
const groupedProjects = await listOfProjects?.reduce(
async (
promisedGroup,
{
guid,
projectOwner: { name: POName },
name,
customer: { name: customerName },
deadline,
calculatedCompletionPercentage,
}
) => {
listOfProjects.map(async (element, index) => {
//accumulator in your case is group
const group = await promisedGroup;
// const element = listOfProjects[index];
sumWorkedHours = await getWorkhoursByProject(element?.guid).then(
(res) => {
return res.reduce((acc, cur) => {
return acc + cur.quantity;
}, 0);
}
);
const filteredEstimatedHours = estimatedHours.filter(
(item) => item.project.guid === element.guid
);
sumEstimatedHours = filteredEstimatedHours.reduce((acc, cur) => {
return acc + cur.workHoursEstimate;
}, 0);
group[POName] = group[POName] || [];
group[POName].push({
guid,
name,
POName,
customerName,
deadline,
calculatedCompletionPercentage,
sumEstimatedHours,
sumWorkedHours,
});
return group;
});
return group;
},
[]
);
return groupedProjects;
};
Best of luck ...

react-query 3 setQueryData doesn't update the cache

I have this query:
export function useCardList() {
return useQuery(['cardList'], fetchCardList());
}
I'm using it in my component:
const { data, isLoading } = useCardList();
// delete
const handleOnDelete = (id: string) => {
const newData = {
...data,
data: data?.data.filter((card) => card.id !== id)
};
queryClient.setQueryData(['cardList'], newData);
};
but setQueryData doesn't work. what do you think? (edited)

Async-await 'Promise {<pending>} with Array.prototype.map

I know there are many questions discuss about the same error and I saw most of them and they didn't fix my problem.
I wrote this code:
const userOrganizationGroups = (organizationGroupsList) => {
if (Array.isArray(organizationGroupsList) && organizationGroupsList.length) {
const result = organizationGroupsList.map(async (element) => {
const { organizationId, groupId } = element;
const { Organizations, Groups } = models;
const organization = await Organizations.findOne(
{ _id: organizationId },
{ name: 1, _id: 0 },
);
const group = await Groups.findOne({ _id: groupId });
return Object.assign({}, {
organizationName: organization.name,
group: group.name,
});
});
return result;
}
};
when I debug the code:
console.log('userOrganizationGroups : ',userOrganizationGroups(list))
I got such a result:
userOrganizationGroups: Promise { <pending> }
I found a similair question: Promise { } - Trying to await for .map and I used the solution mentioned in the question:
const userOrganizationGroups = async (organizationGroupsList) => {
if (Array.isArray(organizationGroupsList) && organizationGroupsList.length) {
const result = await Promise.all(organizationGroupsList.map(async (element) => {
const { organizationId, groupId } = element;
const { Organizations, Groups } = models;
const organization = await Organizations.findOne(
{ _id: organizationId },
{ name: 1, _id: 0 },
);
const group = await Groups.findOne({ _id: groupId });
return Object.assign({}, {
organizationName: organization.name,
group: group.name,
});
}));
return result;
}
How can I fix this issue?
instead of
console.log('userOrganizationGroups : ',userOrganizationGroups(list))
use
userOrganizationGroups(list).then( groups => console.log('userOrganizationGroups : ', groups)
or
(async () => {
const groups = await userOrganizationGroups(list);
console.log('userOrganizationGroups : ', groups);
})();
console.log() was called first because you didn't wait using await or then.
So You should write below instead of
console.log('userOrganizationGroups : ',userOrganizationGroups(list))
;(async () => {
const resultGroups = await userOrganizationGroups(list)
resultGroups.forEach(group => {
console.log(`group: ${JSON.stringfy(group, null, ' ')}`)
})
})()

Categories

Resources