Await doesn't doing the job - javascript

I got this map function on a array:
foldersTreeImagesFiles.map( async (paragliderFolder) => {
const pathfolder = pathImagesParaglider + '/' + paragliderFolder.subfolderName;
const imagesUrl = await paragliderFolder.ImagesParagilder.reduce( async (UrlsImage, image, index) => {
const pathImage = pathfolder + '/' + image;
const folderCoulidinary = 'paraglider/' + paragliderFolder.subfolderName;
const resu = await uploadImage(pathImage, folderCoulidinary);
UrlsImage.name = paragliderFolder.subfolderName
UrlsImage[`photo_${index}`] = resu;
return UrlsImage
}, {})
console.log(imagesUrl);
})
The array exemple :
[
{
subfolderName: 'Arcus_Rs',
ImagesParagilder: [
'swing_arcus2rs_slider_arcus6.jpg',
'swing_arcus2rs_slider_arcus7.jpg',
'swing_arcus2rs_slider_arcuslim4.jpg',
'swing_arcus2rs_slider_arcuslime9.jpg'
],
color: [
'swing_arcus2rs_flame.png',
'swing_arcus2rs_lime.png',
'swing_arcus2rs_nightshade.png',
'swing_arcus2rs_ocean.png'
]
},
{
subfolderName: 'Coden_Pro',
ImagesParagilder: [ 'DSC5495.jpg' ],
color: [ 'Air.png', 'Earth.png', 'Fire.png', 'Water.png' ]
},
{
subfolderName: 'Tonic_2',
ImagesParagilder: [
'DSC5349r.jpg',
'DSC6647r.jpg',
'P1044262r.jpg',
'P1044438r.jpg',
'P1044656r.jpg'
],
color: [ 'Lind.png', 'Mustard.png' ]
}
]
So i got this result :
{
name: 'Arcus_Rs',
photo_0: 'url********'
}
{
name: 'Coden_Pro',
photo_0: 'url********'
}
{
name: 'Tonic_2',
photo_0: 'url********'
}
i got only one photo, i should have more photo, so for me it the await of the reduce who doesn't work.
If i try const imagesUrl = await Promies.all(paragliderFolder.ImagesParagilder.reduce( ect...)
i have a error: TypeError: object is not iterable (cannot read property Symbol(Symbol.iterator))
i don't understand why the console log doesn't wait the end of the reduce.

When you do await in your reducer, the function immediately returns a Promise and quits. At the second iteration, UrlsImage is actually the Promise from the first iteration, not the object you put into the reducer. I think this is not what you want.
Try something like this:
const promises = paragliderFolder.ImagesParagilder.map( async (image, index) => {
const pathImage = pathfolder + '/' + image;
const folderCoulidinary = 'paraglider/' + paragliderFolder.subfolderName;
const resu = await uploadImage(pathImage, folderCoulidinary);
return [`photo_${index}`, resu]
})
const entries = await Promise.all(promises)
const imagesUrl = Object.fromEntries(entries)
This will load each image and give you a tuple from which you can build what I assume you want imagesUrl to look like. Not sure about the name property, it seems like you wanted to override it in every iteration.
Here is a dummy-application using it:
function uploadImage(pathImage, folderCoulidinary){
return new Promise((resolve) => {
resolve('uploaded to:' + pathImage)
})
}
const paragliderFolder = {
ImagesParagilder: [
'img1', 'img2'
]
}
const pathfolder = 'pathfolder'
async function runit(){
const promises = paragliderFolder.ImagesParagilder.map( async (image, index) => {
const pathImage = pathfolder + '/' + image;
const folderCoulidinary = 'paraglider/' + paragliderFolder.subfolderName;
const resu = await uploadImage(pathImage, folderCoulidinary);
return [`photo_${index}`, resu]
})
const entries = await Promise.all(promises)
return Object.fromEntries(entries)
}
runit().then(o => console.log(o))

Hello after many research and trials here what i find.
the array.reduce() send only one promise so we can't use the Promise.all().
Here the trick to use a reduce with array of promise:
const array = [4, 7, 78];
const postsPromise = array.reduce((acc, element, index) => {
return acc.then(async result => {
const post = await fetch(`https://jsonplaceholder.typicode.com/posts/${element}`).then(res => res.json());
return { ...result, [`Id_${index}`]: post.title };
});
}, Promise.resolve({}));
const posts = await postsPromise;
console.log(posts);
The Promise.resolve send a Promise already resolved and we loop over it

Related

Array of fetched items doesn`t show after data is fetched

I have this code:
const fetchPokemonData = async () => {
const data = await fetch('https://pokeapi.co/api/v2/pokemon?limit=151', options)
let pokemons = []
await data.json().then(pokemon => pokemons.push(pokemon.results))
return pokemons}
const getPokemonsUrl = async () => {
const pokemonsUrls = []
const pokemonData = await fetchPokemonData()
pokemonData[0].map(pokemons => pokemonsUrls.push(pokemons.url))
return pokemonsUrls}
const createPokemonObject = async () => {
const urls = await getPokemonsUrl()
const arrayOfPokemons = []
urls.map(async url =>{
const data = await fetch(url)
const pokemon = await data.json()
const { name, id, sprites: {other: {dream_world: {front_default}}}, types, weight, stats } = pokemon
arrayOfPokemons.push(
{
name: name,
id: id,
image: front_default, types: types,
weight: weight,
stats: stats
}
)
})
console.log(arrayOfPokemons) //works
arrayOfPokemons.map(pokemon => console.log(pokemon)) // works only after setTimeout() delay }
The problem is when I try to log each pokemon outside urls.map() array function, there is no each individual Pokemon, because the data isn`t fetched yet (I tested that put by putting arrayOfPokemons inside setTimeout() function and after some delay time, each Pokemon was shown). Can someone please rewrite or explain the way to rewrite this code do that I get all individual pokemons outside of urls.map() function, because that is the best way for me to learn.
There are several issues:
push returns the length of the array, not data. Instead of using push, really map the data with a .map and capture the returned array.
.map(async will execute the async callbacks immediately and continue. There is no waiting for those callbacks to terminate their asynchronous code. Either use a normal for loop or use await Promise.all
Here is a correction of your code:
const fetchPokemonData = async () => {
const data = await fetch('https://pokeapi.co/api/v2/pokemon?limit=151');
const pokemon = await data.json();
return pokemon.results;
}
const getPokemonsUrl = async () => {
const pokemons = await fetchPokemonData();
return pokemons.map(pokemon => pokemon.url);
}
const createPokemonObject = async () => {
console.log("wait for it...");
const urls = await getPokemonsUrl();
const arrayOfPokemons = await Promise.all(urls.map(async url => {
const data = await fetch(url);
const pokemon = await data.json();
const { name, id, sprites: {other: {dream_world: {front_default}}}, types, weight, stats } = pokemon;
return {
name: name,
id: id,
image: front_default, types: types,
weight: weight,
stats: stats
};
}));
console.log(arrayOfPokemons);
}
createPokemonObject();

Using return data from an async function, in another function

I'm doing a little data analysis from chess.com data
I have this code
const getUsernames = async function() {
let response = await chessAPI.getCountryPlayers('RE')
names = [...response.body.players]
//console.log(names)
return names
}
const grabPlayerScores = async function() {
let players = getUsernames()
// let playerStats = [];
for (i = 0; i = players.length; i++) {
let data = await chessAPI.getPlayerStats(i)
console.log(data)
}
}
grabPlayerScores();
I can't get the other function to await the return of the first. I know I should use promises or structure my other function differently but I'm still getting the hang of these types of functions.
As a best practice, you should make sure all of your functions return a value. Use const for variables that will not change, and let for variables that you absolutely will change. Watch out for name = ... where you didn't write const or let, as this makes names into a global -
const getUsernames = async function() {
const response = await chessAPI.getCountryPlayers('RE')
return response.body.players // <- probably no need to copy
}
const grabPlayerScores = async function() {
const players = await getUsernames()
return Promise.all(players.map(p => chessAPI.getPlayerStats(p)))
}
Now grabPlayerScores will be a promise containing an array of all player scores -
grabPlayerScores().then(console.log).catch(console.error)
[ ..., ... ,... ]
Maybe you want the player data combined with the score data in the final output?
const grabPlayerScores = async function() {
const players = await getUsernames()
return Promise.all(players.map(async p => ({
player: p,
scores: await chessAPI.getPlayerStats(p)
})))
}
grabPlayerScores().then(console.log).catch(console.error)
[ { player: "alice", scores: ... },
{ player: "brenda", scores: ... },
{ player: "catherine", scores: ... } ]
Another good practice is to make your functions take parameters. This makes them more reusable in other areas of your program -
const getUsernames = async function(countryCode) {
const response = await chessAPI.getCountryPlayers(countryCode)
return response.body.players
}
const grabPlayerScores = async function(countryCode) {
const players = await getUsernames(countryCode)
return Promise.all(players.map(async p => ({
player: p,
scores: await chessAPI.getPlayerStats(p)
})))
}
Now you pass "RE" as an argument to your function, allowing you to easily reuse this function for other countries -
grabPlayerScores("RE").then(console.log).catch(console.error)
You need to add another await in your grabPlayerScores function to wait for getUsernames to finish.
const grabPlayerScores = async function() {
// Add an 'await' here
let players = await getUsernames();
for(let i = 0; i = players.length; i++) {
let data = await chessAPI.getPlayerStats(i)
console.log(data)
}
}
I changed bit in your code,
const grabPlayerScores = async function () {
let players = await getUsernames();
for (i = 0; i = players.length; i++) {
let data = await chessAPI.getPlayerStats(players[i]);
console.log(data)
}
}

Receiving DiscordJS Promise Object Instead of Resolution For Fetch User Nickname

I am trying to create a ranking list/leaderboard, however when trying to map the values, a promise object and not a string is returned. I understand that with fetch I need to handle the promise using .then(), however I can’t figure out how to pass the resolved promise to item in the map function so that the output is correct.
Intended Output Formatted:
LeaderBoard:
<Username> - <# Of Points>
.
.
.
.
.
<Last Username> - <Last # Of Points>
Code:
const buildLeaderBoard = async () => {
const list = await db.collection("Karma").find({}, {upsert: true}).sort({ karma: -1 }).toArray()
mappedlist = list.map(async function(item){
item = await client.users.fetch(`${item.member}`).then((value) =>{
return `${value.nickname} - ${item.karma}`;
});
return item;
}).join(`\n`);
Current Output:
Array.map() will not work for async functions by itself. You instead can use a for loop:
const buildLeaderBoard = async () => {
const list = await db.collection("Karma")
.find({}, {upsert: true})
.sort({ karma: -1 }).toArray(),
mappedList = [];
for (let i = 0; i < list.length; i++) {
const item = list[i],
value = await client.users.fetch(`${item.member}`)
.then((value) => {
return `${value.nickname} - ${item.karma}`;
});
mappedList.push(value);
}
return mappedList.join("\n");
}
You can also make use of Promise.all():
const buildLeaderBoard = async () => {
const list = await db.collection("Karma")
.find({}, {upsert: true})
.sort({ karma: -1 }).toArray(),
mappedList = list.map(async (item) => {
const data = await client.users.fetch(`${item.member}`)
.then((value) => `${value.nickname} - ${item.karma}`)
return data;
});
return (await Promise.all(mappedList)).join("\n");
}

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

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));
}

How to use the beforeEach in node-tap?

Can someone provide an example on how to use the beforeEach? http://www.node-tap.org/api/
Ideally, an example of the promise version, but a callback version example would also be nice.
Here is a test I created which works fine:
'use strict';
const t = require('tap');
const tp = require('tapromise');
const app = require('../../../server/server');
const Team = app.models.Team;
t.test('crupdate', t => {
t = tp(t);
const existingId = '123';
const existingData = {externalId: existingId, botId: 'b123'};
const existingTeam = Team.create(existingData);
return existingTeam.then(() => {
stubCreate();
const newId = 'not 123'
const newData = {externalId: newId, whatever: 'value'};
const newResult = Team.crupdate({externalId: newId}, newData);
const existingResult = Team.crupdate({externalId: existingId}, existingData);
return Promise.all([
t.equal(newResult, newData, 'Creates new Team when the external ID is different'),
t.match(existingResult, existingTeam, 'Finds existing Team when the external ID exists')
]);
});
})
.then(() => {
process.exit();
})
.catch(t.threw);
function stubCreate() {
Team.create = data => Promise.resolve(data);
}
Before I do anything, I want to persist existingTeam. After it's saved, I want to stub Team.create. After these two things, I want to start actually testing. I think it would be cleaner if instead of using a Promise.all or perhaps duplicating the test code, I could use beforeEach.
How would I convert this to use beforeEach? Or what is an example of its usage?
Simple, just return promise from callback function
const t = require('tap');
const tp = require('tapromise');
const app = require('../../../server/server');
const Team = app.models.Team;
const existingId = '123';
const existingData = {
externalId: existingId,
botId: 'b123'
};
t.beforeEach(() => {
return Team.create(existingData).then(() => stubCreate());
});
t.test('crupdate', t => {
t = tp(t);
const newId = 'not 123'
const newData = {
externalId: newId,
whatever: 'value'
};
const newResult = Team.crupdate({
externalId: newId
}, newData);
const existingResult = Team.crupdate({
externalId: existingId
}, existingData);
return Promise.all([
t.equal(newResult, newData, 'Creates new Team when the external ID is different'),
t.match(existingResult, existingTeam, 'Finds existing Team when the external ID exists')
]);
}).then(() => {
process.exit();
}).catch(t.threw);
function stubCreate() {
Team.create = data => Promise.resolve(data);
}

Categories

Resources