Home > Blockchain >  Best practice of using async for json data
Best practice of using async for json data

Time:11-01

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;
}
  • Related