For a "flights" portfolio app, I am trying to generate tickets for each seat in a plane (so that flights are prepopulated with some people). This is part of a function that also prepopulates flights to the database to set up the data. There are calls to the database to check to see if the Flight Number, as well as Confirmation Numbers are unique, and not pre-existing. In the case of flights, if the flight number already exists, the flight just gets dropped. However, I am running into some issues with the asynchronous aspect of regenerating a confirmation number if one already exists when it is in loops. So essentially, the issues I'm having:
generateTickets returns an empty tickets array
I am not sure how to structure my nested forEach loops to return promises when generating the tickets for each seat, in each row. (In my searches on Stack Overflow, I have found multiple solutions referencing Promise.all with array.map. I've tried to implement these solutions in a variety of ways, but I'm still getting an empty array, and I think I'm just not understanding it well enough.)
I am getting: SyntaxError: Unexpected reserved word on await generateConfirmationNumber() - even though generateConfirmationNumber is an asynchronous function
I don't know what to say about what I've tried. It's a lot. I've searched through every link for the first 2-3 pages of Google, I've scoured Stack Overflow trying to find solutions, and then trying to implement them. I think I'm just having a hard time interpreting how their code works, when this has additional complexity. The solutions I have on my browser tab currently are:
Why doesn't the code after await run right away? Isn't it supposed to be non-blocking?
Pushing Elements into an Array That Is Inside a Callback Function
How to wait a Promise inside a forEach loop
Why does my async function return an empty array
Best way to wait for .forEach() to complete
Using async/await with a forEach loop
How to return values from async functions using async-await from function?
I've been googling and trying to get this to work for 3 days now, and I figured it was time to ask a question. :P So on to the code! (If you see crazy all caps comments, it just helps me notice them easily in the console.)
createFlight - Called by router, starts everything.
async function createFlight(req, res) {
create_flights().then(() => {
console.log('Done')
res.redirect('/')
})
}
create_flights - Async Function that generates a random flight.
const create_flights = async() => {
let flights_array = []
let flights_with_tickets = []
for(let i = 0; i < 1; i++) {
let airline = randomAirline();
let origin = randomAirport();
let destination = randomDestination(origin.code);
let duration = generateDuration()
let departure = generateDeparture()
let departure_date = dateString(departure)
let flight_data = {
number: generateFlightNumber(airline.code),
airline: airline.name,
plane: {
name: randomPlane()
},
origin: origin,
destination: destination,
departure: departure,
departure_date: departure_date,
duration: duration,
gate: generateGate(),
amenities: randomAmenities(duration),
tickets: []
}
const flight = new Flight(flight_data);
flights_array.push(flight)
}
console.log("FLIGHTS ARRAY")
console.log(flights_array)
for(let flight of flights_array) {
console.log(flight)
const flight_with_ticket = await returnTicketsAsync(flight)
console.log(flight_with_ticket)
if(flight_with_ticket) {
const record = await returnFlight(flight_with_ticket)
console.log(record)
if(record) {
console.log("Created Flight")
console.log(record.tickets[0])
}
}
}
}
returnTicketsAsync - Waits for the tickets to be generated. This is always console logging an empty tickets value.
async function returnTicketsAsync(flight) {
console.log("ASYNC TICKETS?!?!?!!?")
let tickets = await generateTickets(flight)
console.log(tickets)
}
returnFlight - This checks to see if the flight's confirmation number is already in the database. If it is, we just skip this flight and keep moving on. Otherwise, we save it to the database.
const returnFlight = flight => {
return new Promise((resolve, reject) => {
Flight.find({$or:[{number: flight.number}]}, function(error, records) {
if(records.length) {
console.log("Similar flight already exists")
resolve()
} else {
flight.save(function (error, result) {
if(error) {
console.log("ERROR:" + error)
resolve()
} else {
resolve(result)
console.log("Flight " + result.number + " successfully saved.")
}
})
}
})
})
}
generateTickets(flight) - The source of all my troubles. The Flight has a plane map, with sections (first class, preferred class, economy). Each section has rows with seats.
So I need to loop through first class, preferred class, and economy class, generate the tickets for all the seats. There is one function, generateConfirmationNumber() that generates the number, and checks the database for a result. If there's no result, the confirmation number is good to be used, and it returns that. I need to await for that to be done, but I'm getting a SyntaxError here.
async function generateTickets(flight) {
let tickets = []
let prices = generatePrice()
const first_class = await Promise.all(_.map(flight.plane.sections.first_class.rows, async function (row, i, rows) {
let first_class_tickets = await _.map(row, function(seat) {
if(!seat.isle) {
let passenger = generatePassenger()
let confirmation_number = await generateConfirmationNumber()
let tickets = {
confirmation_number: '',
tracking_number: generateTrackingNumber(),
data: generateDataNumber(),
options: '1ST CL',
flight_class: 'first',
seat: {
isle: seat.letter,
number: i + 1
},
boarding_zone: generateBoardingZone(),
price: prices.first,
passenger: passenger
}
if(passenger.first_name !== '' || passenger.first_name.length > 1) {
ticket.confirmation_number = confirmation_number
}
console.log(ticket)
tickets.push(ticket)
}
})
}))
const preferred_class = await Promise.all(_.map(flight.plane.sections.preferred_class.rows, async function (row, i, rows) {
let preferred_class_tickets = await _.map(row, function(seat) {
if(!seat.isle) {
let passenger = generatePassenger()
let confirmation_number = await generateConfirmationNumber()
let tickets = {
confirmation_number: '',
tracking_number: generateTrackingNumber(),
data: generateDataNumber(),
options: 'PREF PLUS',
flight_class: 'preferred',
seat: {
isle: seat.letter,
number: i + flight.plane.sections.first_class.total_rows + 1
},
boarding_zone: generateBoardingZone(),
price: prices.preferred,
passenger: passenger
}
if(passenger.first_name !== '' || passenger.first_name.length > 1) {
ticket.confirmation_number = confirmation_number
}
console.log(ticket)
tickets.push(ticket)
}
})
}))
const economy_class = await Promise.all(_.map(flight.plane.sections.economy_class.rows, async function (row, i, rows) {
let economy_class_tickets = await _.map(row, function(seat) {
if(!seat.isle) {
let passenger = generatePassenger()
let confirmation_number = await generateConfirmationNumber()
let tickets = {
confirmation_number: '',
tracking_number: generateTrackingNumber(),
data: generateDataNumber(),
options: 'PREF PLUS',
flight_class: 'preferred',
seat: {
isle: seat.letter,
number: i + flight.plane.sections.first_class.total_rows + flight.plane.sections.preferred_class.total_rows + 1
},
boarding_zone: generateBoardingZone(),
price: prices.economy,
passenger: passenger
}
if(passenger.first_name !== '' || passenger.first_name.length > 1) {
ticket.confirmation_number = confirmation_number
}
console.log(ticket)
tickets.push(ticket)
}
})
}))
console.log("Tickets")
console.log(ticekts)
}
generateConfirmationNumber - Just throwing this here, too.
async function generateConfirmationNumber() {
let letters = 'ABCDEFGHJKLMNPRSTUVWXYZ'
let number = Math.floor(Math.random() * 10000)
let confirmation = letters[Math.floor(Math.random() * letters.length)] + letters[Math.floor(Math.random() * letters.length)] + letters[Math.floor(Math.random() * letters.length)] + number
return new Promise((resolve, reject) => {
Flight.findOne({'tickets.confirmation_number': confirmation}, (err, result) => {
if (err) console.log(error)
else if (result) return resolve(generateConfirmationNumber())
else return resolve(confirmation)
})
})
}
I'm at a loss for how to get this working. The goal is simply to generate the tickets, wait for the generateConfirmationNumber() to finish, and push all the tickets into a "tickets" array for that flight. Then, return that array so it can be $set to the flight, and so then, the flight can be pushed into the database.
The main issue your codes doesn't work because you try to Promise.all() to a array of the ticket below.
Promise.all only working on promises object, but you can see the ticket itself is not a purely Promise object. Also, the map() function will not await the promise in the loop. If you want to await a promise in a loop, you need to use for loop.
let confirmation_number = await generateConfirmationNumber();
let ticket = {
confirmation_number: confirmation_number,
tracking_number: generateTrackingNumber(),
data: generateDataNumber(),
options: "1ST CL",
flight_class: "first",
seat: {
isle: seat.letter,
number: i + 1,
},
boarding_zone: generateBoardingZone(),
price: prices.first,
passenger: generatePassenger(),
};
The below codes doesn't work because ticket is not a promise object.
Promise.all([ticket1,ticket2,ticket3]);
To make it work, you have two options.
Promises in Parallel: Promise.all all the promises object and use array#map() to create a array of ticket objects.
Promises in Series: await each promise and construct the ticket one by one.
If you want to do a Promises in Parallel way, you can do like below:
const arrayOfPromises = flight.plane.sections.first_class.rows.flatMap(row =>
row.map(seat => generateConfirmationNumber())
);
const confirmationNumbers = await Promise.all(arrayOfPromises);
const tickets = confirmationNumbers.map(cfmNumber => ({
confirmation_number: cfmNumber,
tracking_number: generateTrackingNumber(),
data: generateDataNumber(),
options: "1ST CL",
flight_class: "first",
seat: {
isle: seat.letter,
number: i + 1,
},
boarding_zone: generateBoardingZone(),
price: prices.first,
passenger: generatePassenger(),
}));
The answer I use is 2nd option(Promises in Series).
I use for of loop to await each promise and create a ticket from it.
In the end, I add ticket to a array tickets.
async function generateTickets(flight) {
let tickets = [];
let prices = generatePrice();
const firstClass = [];
for (const row of flight.plane.sections.first_class.rows) {
for (const seat of row) {
if (!seat.isle) {
const passenger = generatePassenger();
const confirmation_number = await generateConfirmationNumber();
const ticket = {
confirmation_number: "",
tracking_number: generateTrackingNumber(),
data: generateDataNumber(),
options: "1ST CL",
flight_class: "first",
seat: {
isle: seat.letter,
number: flight.plane.sections.first_class.rows.indexOf(row) + 1,
},
boarding_zone: generateBoardingZone(),
price: prices.first,
passenger: passenger,
};
if (passenger.first_name !== "" || passenger.first_name.length > 1) {
ticket.confirmation_number = confirmation_number;
}
firstClass.push(ticket);
}
}
}
console.log(firstClass);
const preferredClassTickets = [];
for (const row of flight.plane.sections.preferred_class.rows) {
for (const seat of row) {
if (!seat.isle) {
let passenger = generatePassenger();
let confirmation_number = await generateConfirmationNumber();
let ticket = {
confirmation_number: "",
tracking_number: generateTrackingNumber(),
data: generateDataNumber(),
options: "PREF PLUS",
flight_class: "preferred",
seat: {
isle: seat.letter,
number: flight.plane.sections.preferred_class.rows.indexOf(row) + flight.plane.sections.first_class.total_rows + 1,
},
boarding_zone: generateBoardingZone(),
price: prices.preferred,
passenger: passenger,
};
if (passenger.first_name !== "" || passenger.first_name.length > 1) {
ticket.confirmation_number = confirmation_number;
}
preferredClassTickets.push(ticket);
}
}
}
const economyClass = [];
for (const row of flight.plane.sections.economy_class.rows) {
for (const seat of row) {
if (!seat.isle) {
let passenger = generatePassenger();
let confirmation_number = await generateConfirmationNumber();
let ticket = {
confirmation_number: "",
tracking_number: generateTrackingNumber(),
data: generateDataNumber(),
options: "PREF PLUS",
flight_class: "preferred",
seat: {
isle: seat.letter,
number:
flight.plane.sections.economy_class.rows.indexOf(row) +
flight.plane.sections.first_class.total_rows +
flight.plane.sections.preferred_class.total_rows +
1,
},
boarding_zone: generateBoardingZone(),
price: prices.economy,
passenger: passenger,
};
if (passenger.first_name !== "" || passenger.first_name.length > 1) {
ticket.confirmation_number = confirmation_number;
}
economyClass.push(ticket);
}
}
}
tickets.push(...firstClass, ...preferredClassTickets, ...economyClass);
return tickets;
}
async function generateConfirmationNumber() {
let letters = "ABCDEFGHJKLMNPRSTUVWXYZ";
let number = Math.floor(Math.random() * 10000);
let confirmation =
letters[Math.floor(Math.random() * letters.length)] +
letters[Math.floor(Math.random() * letters.length)] +
letters[Math.floor(Math.random() * letters.length)] +
number;
return new Promise((resolve, reject) => {
Flight.findOne(
{ "tickets.confirmation_number": confirmation },
(err, result) => {
if (err) reject(error);
// else if (result) return resolve(generateConfirmationNumber());
else if (result) return resolve(result);
else return resolve(confirmation);
}
);
});
}
const returnFlight = flight => {
return new Promise((resolve, reject) => {
Flight.find(
{ $or: [{ number: flight.number }] },
function (error, records) {
if (error) reject(error);
if (records.length) {
console.log("Similar flight already exists");
resolve(records);
} else {
flight.save(function (error, result) {
if (error) {
console.log("ERROR:" + error);
reject(error);
} else {
console.log("Flight " + result.number + " successfully saved.");
resolve(result);
}
});
}
}
);
});
};
const create_flights = async () => {
let flights_array = [];
// let flights_with_tickets = [];
for (let i = 0; i < 1; i++) {
let airline = randomAirline();
let origin = randomAirport();
let destination = randomDestination(origin.code);
let duration = generateDuration();
let departure = generateDeparture();
let departure_date = dateString(departure);
let flight_data = {
number: generateFlightNumber(airline.code),
airline: airline.name,
plane: {
name: randomPlane(),
},
origin: origin,
destination: destination,
departure: departure,
departure_date: departure_date,
duration: duration,
gate: generateGate(),
amenities: randomAmenities(duration),
tickets: [],
};
const flight = new Flight(flight_data);
flights_array.push(flight);
}
console.log("FLIGHTS ARRAY");
console.log(flights_array);
for (let flight of flights_array) {
const tickets = await generateTickets(flight);
if (tickets) {
flight.set({
tickets: tickets
})
const record = await returnFlight(flight);
console.log(record);
if (record) {
console.log("Created Flight");
console.log(record.tickets);
}
}
}
};
async function createFlight(req, res) {
try {
await create_flights();
console.log("Done");
res.redirect("/");
} catch (err) {
console.log(err);
}
}
i have a query "details" which is like below
query details(
$id: ID!
) {
something(id: $id) {
id
itemId
typesId
itemDetails {
id
name
}
typesDetails {
id
name
}
}
}
i have defined the types like below
type itemDetails {
id: String,
name: String,
}
type typesDetails {
id: String,
name: String,
}
type something {
id: ID!
itemId: ID
typesId: [ID!]
itemDetails: itemDetails
typesDetails: [typesDetails]
}
on the resolvers side (graphql) i have to field resolve the itemDetails (with the itemId i recieve from backend). this itemId can be null or can have some string value like example '1'.
and typesDetails with the typesId i receive from backend. typesId can be null or array of ids like example ['1','2',...]
const resolvers: Resolvers = {
something: {
itemDetails: async(parent, args, { dataSources: { itemsAPI} }) => {
const itemId = get (parent, 'itemId'); //can be null or string value
if(itemId) {
const { data } = await itemsAPI.getItems();
const item = data.filter((item: any) =>
itemId === item.id
); //filter the data whose id is equal to itemId
return {
id: item[0].id,
name: item[0].name,
}
}else { // how to rewrite this else part
return {}:
}
},
typesDetails: async (parent, args, { dataSources: {assetTypesAPI} }) => {
const typesId = get(parent, 'typesId');
if (typesId) {
const allTypes = await typesAPI.getTypes();
const res = typesId.map((id: any) => allTypes.find((d) => d.id === id)); //filter
//allTypes that match typesId
const final = res.map(({id, name}: {id:string, name:string}) => ({id,name}));
//retreive the id and name fields from res array and put it to final array
return final;
} else { // how to rewrite this else part
return [{}];
}
}
}
The above code works. but the code looks clumsy in the way i return empty array if no itemId and typesId returned from backend.
how can i handle the case for itemDetails field if itemId is null from backend and typesDetails field if typesId is null from backend.
could someone help me with this. thanks.
The question is more about graphql than it is react and typescript. You do well to change the tag 🏷 of the question for proper matching.
The perfect solution for me would be validating the itemId in the case of itemDetails. If the value is null, throw an error or return an empty object like you're doing in the else section of the itemDetails.
I don't think itemId is coming from the backend in the case above, it should be coming from the argument passed to the query itemDetails
The same applies to the typesDetails.
Using try and catch can help catch any error during the async operations to the API (database).
const resolvers: Resolvers = {
something: {
itemDetails: async(parent, args, { dataSources: { itemsAPI} }) => {
try {
const itemId = get (parent, 'itemId'); //can be null or string value
if(itemId) {
const { data } = await itemsAPI.getItems();
const item = data.filter((item: any) =>
itemId === item.id
); //filter the data whose id is equal to itemId
return {
id: item[0].id,
name: item[0].name,
}
}else { // how to rewrite this else part
return {}:
}
} catch(error) {
throw new Error('something bad happened')
}
},
typesDetails: async (parent, args, { dataSources: {assetTypesAPI} }) => {
try {
const typesId = get(parent, 'typesId');
if (typesId) {
const allTypes = await typesAPI.getTypes();
const res = typesId.map((id: any) => allTypes.find((d) => d.id === id)); //filter
//allTypes that match typesId
const final = res.map(({id, name}: {id:string, name:string}) => ({id,name}));
//retreive the id and name fields from res array and put it to final array
return final;
} else { // how to rewrite this else part
return [{}];
}
} catch (error) {
throw new Error('something happened')
}
}
}
How can I replace the oldguild(old server's name) to a new one when server updated? I tried with GuildID(discord server id) and everything but nothing seem to work. When the bot saves it, then looks like this in the MongoDB Compass: Screenshot, and for example this is how it looks like in MongoDB Compass normally: Old name server, but I want it look like this New name server.
This is my code.
client.on("guildUpdate", (oldguild, newguild) => {
var name = [oldguild.name, newguild.name];
if(name[0] == null) {
name[0] = oldguild.name
}
if(name[1] == null) {
name[1] = oldguild.name
}
if(oldguild.name !== newguild.name)
{
async function guildUpdated() {
const servername = new setprefixModel ({
_id: mdb.Types.ObjectId(),
GuildID: setprefixModel.GuildId,
Guild: oldguild.name,
Prefix: setprefixModel.Prefix
});
const reqservername = await setprefixModel.findOne({ Guild: oldguild.name });
if(!reqservername) {
return await servername.save();
}
else {
const updatedDocument = await setprefixModel.findOneAndUpdate(
{ Guild: oldguild.name },
{ Guild: newguild.name },
{ new: true }
);
updatedDocument;
}
}
guildUpdated();
}
})
client.on("guildUpdate", (oldguild, newguild) => {
var name = [oldguild.name, newguild.name];
if(name[0] == null) {
name[0] = oldguild.name
}
if(name[1] == null) {
name[1] = oldguild.name
}
if(oldguild.name !== newguild.name)
{
async function guildUpdated() {
const servername = new setprefixModel ({
_id: mdb.Types.ObjectId(),
GuildID: String,
Guild: oldguild.name,
Prefix: String
});
const reqservername = await setprefixModel.findOne({ Guild: oldguild.name });
if(!reqservername) {
return await servername.save();
}
else {
const updatedDocument = await setprefixModel.findOneAndUpdate(
{ Guild: oldguild.name },
{ Guild: newguild.name },
{ new: true }
);
updatedDocument;
}
}
guildUpdated();
}
})
Changed at const servername.
This question already has answers here:
How to use promise in forEach loop of array to populate an object
(5 answers)
Closed 2 years ago.
On the firebase functions, I have next code:
app.post('/licence', (req, res) => {
let { email, machine_id, product_id } = req.body
let arr_product_ids = product_id.split(",").map(function (val) { return {product_id: val}; });
let res_to_print = '';
return new Promise((resolve, reject) => {
arr_product_ids.forEach(function(n){
res_to_print = asyncGetLicences(n.product_id, email, machine_id)
console.log('res_to_print')
console.log(res_to_print)
});
}).then((state) => {
console.log(state)
})
.catch((error) => {
console.log(error)
});
I need to call query two times to firebase query! So I call it in foreach loop.
Here is the function that needs to be called twice:
function asyncGetLicences(product_id, email, machine_id) {
licences_to_print = []
db.collection('licences', 'desc').where('email', '==', email).where('product_id', '==', product_id).get()
.then(data => {
let licences = []
data.forEach(doc => {
console.log(doc.data().email);
licences.push({
id: doc.id,
email: doc.data().email,
product: doc.data().product,
createdAt: doc.data().createdAt
});
});
if(typeof this.licences !== 'undefined' && this.licences.length > 0){
let string = email+machine_id+product_id+'55';
let api = md5(string);
let uppercase = api.toUpperCase()+'-';
licences_to_print.push(uppercase);
return licences_to_print
//return res.send('"'+uppercase+'"');//res.json(this.licences);
} else {
return licences_to_print
//return res.status(200).send('nothing to find');
}
})
}
I'm struggling with this simple promise...I had this in PHP and it was very easy, but node.js and firebase I got stuck!
Add all the promises in an array and insert into Promise.all() and then return this in the main function. This will collectively get the return from each promises asynchronously and return a single collective response.
app.post('/licence', (req, res) => {
let { email, machine_id, product_id } = req.body
let arr_product_ids = product_id.split(",").map(function (val) { return {product_id: val}; });
let res_to_print = '';
const promises = [] // Empty array
arr_product_ids.forEach(function(n){
promises.push(asyncGetLicences(n.product_id, email, machine_id));
});
return Promise.all(promises).then(res_to_print => {
console.log('res_to_print')
console.log(res_to_print)
}).catch((error) => {
console.log(error)
});
The second function:
function asyncGetLicences(product_id, email, machine_id) {
licences_to_print = []
return new Promise((resolve, reject) => {
db.collection('licences', 'desc').where('email', '==', email).where('product_id', '==', product_id).get()
.then(data => {
let licences = []
data.forEach(doc => {
console.log(doc.data().email);
licences.push({
id: doc.id,
email: doc.data().email,
product: doc.data().product,
createdAt: doc.data().createdAt
});
});
if(typeof this.licences !== 'undefined' && this.licences.length > 0){
let string = email+machine_id+product_id+'55';
let api = md5(string);
let uppercase = api.toUpperCase()+'-';
licences_to_print.push(uppercase);
resolve(licences_to_print)
//return res.send('"'+uppercase+'"');//res.json(this.licences);
} else {
resolve(licences_to_print)
//return res.status(200).send('nothing to find');
}
}))
Hello guys I have the following query but it returns only an array with skills instead the object with array inside
To make it clearer
EndorsedSkill response
{
_id
userId
skills: [
{_id},
{_id}
]
}
Skill response
{
_id
name
}
What I want to receive is
{
_id
userId
skills: [
{_id, name},
{_id, name}
]
}
The query looks like this
getUsersSkills: async (_, { u_id, ...args }, { user }) => {
try {
await requireAuth(user);
const p1 = Skill.find({}).sort({ createdAt: -1 });
const p2 = EndorsedSkill.findOne({ userId: u_id });
const [skills, endorsed] = await Promise.all([p1, p2]);
const checkEndorsedSkills = await skills.reduce((arr, skill) => {
const s = skill.toJSON();
if (endorsed.skills.some(s => s.equals(skill._id))) {
arr.push({
...s,
endorsed: true
});
} else {
arr.push({
...s,
endorsed: false
});
}
return arr;
}, []);
const endorsedSkills = checkEndorsedSkills.filter(
skill => skill.endorsed === true
);
return endorsedSkills;
} catch (error) {
throw error;
}
},
Can you please mention that how you connected to mongoDB?