I am trying to save data received as JSON in DB. But my method is unnecessarily time-consuming, so I write the question.
Here's my code:
const data = [{
id: 1,
name: 'foo',
items: [{
name: 'foo-1'
}, {
name: 'foo-2'
}]
}, {
id: 2,
name: 'bar',
items: [{
name: 'bar-1'
}, {
name: 'bar-2'
}]
}]
const createUser = (data) => {
console.log('start create user:', data)
return new Promise(resolve => {
setTimeout(() => {
console.log('create user: ', data.name)
resolve(data.id);
}, 2000);
});
}
const createUserMeta = (id, name) => {
console.log('start create user meta:', name)
return new Promise(resolve => {
setTimeout(() => {
console.log('create user meta: ', id, name)
resolve(`set ${id}: ${name}`)
}, 3000)
})
}
// Logic
async function asyncCall() {
const result = []
console.log('calling')
for (const i of data) {
console.log(i)
const usersId = await createUser(i)
for (const item of i.items) {
const fn2Return = await createUserMeta(usersId, item.name)
result.push(fn2Return)
}
}
console.log(result)
}
asyncCall();
result is:
calling
{ id: 1, name: 'foo', items: [ { name: 'foo-1' }, { name: 'foo-2' } ] }
start create user: { id: 1, name: 'foo', items: [ { name: 'foo-1' }, { name: 'foo-2' } ] }
// wait 2 sec
create user: foo
start create user meta: foo-1
// wait 3 sec
create user meta: 1 foo-1
start create user meta: foo-2
// wait 3 sec
create user meta: 1 foo-2
{ id: 2, name: 'bar', items: [ { name: 'bar-1' }, { name: 'bar-2' } ] }
start create user: { id: 2, name: 'bar', items: [ { name: 'bar-1' }, { name: 'bar-2' } ] }
// wait 2 sec
create user: bar
start create user meta: bar-1
// wait 3 sec
create user meta: 2 bar-1
start create user meta: bar-2
// wait 3 sec
create user meta: 2 bar-2
[ 'set 1: foo-1', 'set 1: foo-2', 'set 2: bar-1', 'set 2: bar-2' ]
createUserMeta should wait for createUser to exit before starting it. But there is no need to wait for data[0] to complete to save data[1]. How can I run them in parallel?
CodePudding user response:
Wrap creating the user and related metadata to a function such as
async function createUserAndMeta(user) {
console.log(user);
const usersId = await createUser(user);
const result = [];
for (const item of user.items) {
const fn2Return = await createUserMeta(usersId, item.name);
result.push(fn2Return);
}
return result;
}
and then use map
to map all users to promises and Promise.all()
to await on them:
async function asyncCall() {
const results = await Promise.all(data.map(createUserAndMeta));
const result = results.flat(); // flatten array of arrays
console.log(result);
}
Do note that if you have a lot of data
, this will cause all requests to be done in parallel (e.g. for 50 users, it'll try to make 50 parallel createUser
calls). To limit the concurrency to e.g. 5 at a time, you may want to use e.g. the p-queue
module.
CodePudding user response:
async function createUser(data) {
const pk = await insertUser(data.id, data.name);
const result = await Promise.all(data.items.map(createUserMeta);
return {pk, result};
}
async function asyncCall() {
const result = await Promise.all(data.map(createUser));
return result;
}