How to run fetch() in a loop? - javascript

I am new to nodejs and promise based request. I want to fetch the data from a remote server in a loop, and then create a JSON object from all fetched data.
const fetch = require('node-fetch');
const users = [];
const ids = await fetch('https://remote-server.com/ids.json');
console.log(ids);
// [1,2,3]
ids.forEach(id => {
var user = await fetch(`https://remote-server.com/user/${id}.json`);
users.push(user);
});
console.log(users);
expected output
[
{
name: 'user 1',
city: 'abc'
},
{
name: 'user 2',
city: 'pqr'
},
{
name: 'user 3',
city: 'xyz'
}
]

So to launch in parallel:
const ids = await fetch('https://remote-server.com/ids.json');
const userPromises = ids.map(id => fetch(`https://remote-server.com/user/${id}.json`));
const users = await Promise.all(userPromises);
to launch in sequence:
const users = [];
const ids = await fetch('https://remote-server.com/ids.json');
for(const id of ids){
const user = await fetch(`https://remote-server.com/user/${id}.json`);
users.push(user);
}

You forgot to add async in the forEach:
ids.forEach(async (id) => { // your promise is in another function now, so you must specify async to use await
var user = await fetch(`https://remote-server.com/user/${id}.json`);
users.push(user);
});

Related

Firestore - Web version 9 - get all documents in collection whose id is in array of ids

I'm new to Firestore and trying to form a query to return all documents whose doc.id is in an array of ids.
So something like this:
const getAlbumSongs = async (songUids) => {
let albumSongs = [];
const q = query(collection(db, 'songs'), where('id', 'in', songUids));
const snap = await getDocs(q);
if (snap) {
for (const doc of snap) {
albumSongs.push({
songId: doc.id,
albumId: doc.data().album_id,
authorId: doc.data().author_id,
songTitle: doc.data().song_title,
filePath: doc.data().file_path,
});
}
}
return albumSongs;
};
I should note I'm calling the getAlbumSongs() function from within a getAlbums() function, like this:
async function getAlbums() {
setIsLoadingAlbums(true);
const snap = await getDocs(collection(db, 'albums'));
setIsLoadingAlbums(false);
let albumsData = [];
if (snap) {
snap.forEach(async (doc) => {
albumsData.push({
ablumId: doc.id,
albumTitle: doc.data().album_title,
albumCoverUrl: doc.data().cover_url || 'https://picsum.photos/300',
albumSongs: await getAlbumSongs(doc.data().song_uids), // this might be problematic?
});
});
console.log('albumsData', albumsData);
return albumsData;
}
}
Your current code queries on a field called id in each document. If you want the document ID itself, that is part of the metadata of the document and you'll want to query on documentId() for that:
const q = query(collection(db, 'songs'), where(documentId(), 'in', songUids));

removing an object from firestore 9 array using arrayRemove()?

I am trying to remove an object from array in in firestore, but encountered an obstacle what are the requirement or the reference to do the removal ? does one key value in the object sufficient to do the remove or should the object by identical to the one that is getting removed ?
const deleteWeek = async () => {
const docRef = doc(db, 'Weeks', id);
await updateDoc(docRef, {
weeks: arrayRemove({
weekId: '7518005f-7b10-44b6-8e0a-5e41081ee064',
}),
});
};
deleteWeek();
}
however week in data base looks like this
{name ,"Week 2"
days : [/*data all kinds*/]
weekId : "7518005f-7b10-44b6-8e0a-5e41081ee064"}
If it's an array of object, then you need to know the whole object to use arrayRemove() For example, if the a document looks like this:
{
...data
weeks: [
{
name: "Week 2",
days: [/*data all kinds*/]
weekId: "7518005f-7b10-44b6-8e0a-5e41081ee064"}
}
]
}
You'll have to pass the entire week object in arrayRemove(). It might be better to store such data in sub-collections instead so you can query/delete a specific one.
Since there is no function in firestore to delete only a element in array, you need to make arrayRemove refer to the same object you want to delete, then create a new object and insert it with arrayUnion method
in my case, i use to below
const { leave,date,email } = req.body;
const attendanceRef = admin.firestore().collection('Attendance').doc(`${email}`);
const attendanceData = await attendanceRef.get();
const attendanceRecord = attendanceData.data().attendance;
const removeTarget = attendanceRecord.find((item) => item.date === date);
await attendanceRef.update({
attendance: admin.firestore.FieldValue.arrayRemove(removeTarget),
})
const obj = {
...removeTarget,
"leave": leave,
}
await attendanceRef.set({
attendance: admin.firestore.FieldValue.arrayUnion(obj),
},{ merge: true })
const newAttendance = await attendanceRef.get();
const newAttendanceRecord = newAttendance.data().attendance;
return await res.json({
message: '퇴근시간이 저장되었습니다.',
attendance:newAttendanceRecord
});
after update, it maybe if error occured.
if error occured, you need all working cancel.
this case, you may want to use batch method
const admin = require('firebase-admin');
module.exports = async function(req,res) {
const { leave,date,email } = req.body;
const batch = admin.firestore().batch();
const attendanceRef = admin.firestore().collection('Attendance').doc(`${email}`);
const attendanceData = await attendanceRef.get();
const attendanceRecord = attendanceData.data().attendance;
const removeTarget = attendanceRecord.find((item) => item.date === date);
// await attendanceRef.update({
// attendance: admin.firestore.FieldValue.arrayRemove(removeTarget),
// })
batch.update(
attendanceRef,{ attendance: admin.firestore.FieldValue.arrayRemove(removeTarget) }
)
const obj = {
...removeTarget,
"leave": leave,
}
// await attendanceRef.set({
// attendance: admin.firestore.FieldValue.arrayUnion(obj),
// },{ merge: true })
batch.set(
attendanceRef, { attendance: admin.firestore.FieldValue.arrayUnion(obj) },{ merge: true }
)
await batch.commit();
const newAttendance = await attendanceRef.get();
const newAttendanceRecord = newAttendance.data().attendance;
return await res.json({message: '퇴근시간이 저장되었습니다.',attendance:newAttendanceRecord});
}
hope help this for you

ReactJS: How to update the data in array state

const [rowData, setRowData] = useState([]);
const old = {id: 'stud1', name: 'jake', room: '2'};
const newData = {name: 'jake', room: '3A'};
useEffect(() => {
let ignore = false;
(async function getUsers() {
let response = await getAll({ length: 999 });
if (!ignore) setRowData(response['data']['data']);
})()
return () => {
ignore = true;
}
}, []);
(async function updateItem() {
await update(oldData.id, newData).then(response => {
//value of response['data'] = {id: 'stud1', name: 'jake', room: '3A'}
setRowData(arr => [...arr, response['data']]);
}).catch(err => {
console.log(err);
})
})()
How to update the array list without appending a new data. cause I tried this setRowData(arr => [...arr, response['data']]); then it keeps appending a data.
Instead it will update the value in the array it will append on it.
Since you want to update one item within the state array, you can use map and update the item based on the id like below
(async function updateItem() {
await update(oldData.id, newData).then(response => {
setRowData(arr => arr.map(item => {
if (item.id === response.data.id) {
return response.data;
}
return item;
});
}).catch(err => {
console.log(err);
})
})()
I do not think you need a spread operator.. you can just use array.map().. maybe something like this will help you..
const old = [{id: 'stud1', name: 'jake', room: '2'},{id: 'stud2', name: 'jack', room: '2'}];
const newData = {id: 'stud1', name: 'jakey', room: '3A'};
const x = old.map((stud) => {
if(stud.id === newData.id){
stud = newData;
}
return stud;
});
console.log(x);
then you can use the x for setRowData(x)

Mongoose script to seed database hangs

I have the following script:
const db = require('../db')
const User = require('../models/user')
db.on('error', console.error.bind(console, 'MongoDB connection error:'))
const main = async () => {
const users = [
new User({ name: 'Benny', age: 28, status: 'active' }),
new User({ name: 'Claire', age: 28, status: 'active' })
]
const newUsers = async () => {
await users.forEach(async user => await user.save())
}
await newUsers()
console.log("Created users!")
}
const run = async () => {
await main()
process.exit(0)
}
run()
For some reason process.exit() executes before main() resolves and therefore I get no users created.
If I remove process.exit() my script works but hangs.
How do I get my script to work and exit once done executing?
Awaiting users.forEach() doesn't do anything because forEach doesn't have a return value, so there's nothing to await. It's likely iterating through the entire list and then exiting immediately, which then returns from main and calls process.exit() before the .save()s have a chance to execute.
What you can do, however, is wait for all the promises to finish by using Promise.all(). This will require you to map each user creation into a Promise, but that's what your User.save function is doing anyway, it's returning a Promise. Here's an example of how you can do that:
function save(user) {
// do something async here
return new Promise((resolve, reject) => {
setTimeout(() => {
console.log(`${user.name} Saved!`);
resolve()
}, 1500);
});
}
const main = async () => {
const users = [
{ name: 'Benny', age: 28, status: 'active' },
{ name: 'Claire', age: 28, status: 'active' }
]
// map async processes into an array of promises
const newUsers = users.map(user => save(user));
// await the resolution of all promises, then proceeed
await Promise.all(newUsers);
console.log("Created users!")
}
const run = async () => {
await main()
console.log("done")
}
run()
The distinct benefit of doing it this way is that the database calls will happen in parallel, which will make your seeding process much faster. Conceptually, what you were trying to do would have waited for each database call to finish before starting the next one (aka running serially). If you need them to execute in order, then that's a good thing, but it doesn't seem like your use-case requires that.
Co-worker of mine proposed this solution:
const db = require('../db')
const User = require('../models/user')
db.on('error', console.error.bind(console, 'MongoDB connection error:'))
const main = async () => {
const users = [
new User({ name: 'Benny', age: 28, status: 'active' }),
new User({ name: 'Claire', age: 28, status: 'active' })
]
const newUsers = async () => {
await users.reduce(async (promise, user) => {
await user.save()
return promise
}, Promise.resolve())
}
await newUsers()
console.log("Created users!")
}
const run = async () => {
await main()
process.exit()
}
run()
Here is my solution - would love some feedback on improving it!
const db = require('../db')
const User = require('../models/user')
db.on('error', console.error.bind(console, 'MongoDB connection error:'))
const main = async () => {
const users = [
{ name: 'Benny', age: 28, status: 'active' },
{ name: 'Claire', age: 28, status: 'active' }
]
await User.insertMany(users)
console.log("Created users!")
}
const run = async () => {
await main()
db.close()
}
run()

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