1252748
1252748

Reputation: 15372

Use async/await to combine concurrent and sequential requests

Using async/await, I'm trying to perform three requests as quickly as possible.

getIndependentThingOne and getIndependentThingTwo return data without submitting any parameters to their endpoints. getDependentThing, however, requires the id property of the first element of a list returned by getIndependentThingTwo. I have somewhat tried to follow in principle, what's outlined in this pizza post. At least in spirit.

const getIndependentThingOne = async () => {
  const url = `${root}/1`
  return axios.get(url)
}

const getIndependentThingTwo = async () => {
  const url = `${root}/2`
  return axios.get(url)
}

const getDependentThing = async ([{ id }]) => {
  const url = `${root}/3/${id}`
  return axios.get(url)
}

const getIndependentThingOneAndDependentThing = async () => {
  const { data: listFromIndependentThingTwo } = await getIndependentThingTwo()
  const { data: listFromDependentThing } = await getDependentThing(
    listFromIndependentThingTwo
  )
  return {
    listFromIndependentThingTwo,
    listFromDependentThing
  }
}

const getAllData = async () => {
  const [
    { data: listFromIndependentThingOne },
    { listFromIndependentThingTwo, listFromDependentThing }
  ] = await Promise.all([
    getIndependentThingOne(),
    getIndependentThingOneAndDependentThing()
  ])
  console.log('DONE')
  console.log({ listFromIndependentThingOne })
  console.log({ listFromIndependentThingTwo })
  console.log({ listFromDependentThing })
}

getAllData()

While this works, I wonder if it is the most optimal way to structure these requests. Returning an object of the last two values and destructuring them here seems a little off somehow

const [
  { data: listFromIndependentThingOne },
  { listFromIndependentThingTwo, listFromDependentThing }
]

Is there a more idiomatic pattern for doing this sort of operation using async/await?

Upvotes: 1

Views: 1276

Answers (4)

user9315861
user9315861

Reputation:

The basic pattern is:

async function getStuff() {
  // Kick off thing one request, don't wait!
  const thingOnePromise = getIndependentThingOne();

  // Kick off things two request, don't wait quite yet.
  const [{id}] await getIndependentThingTwo();

  await dependentThingThree(id);
  await thingOnePromise;
}

In other words, you kick off the first request, without awaiting it. You await it later, when you have to, before the function completes.

Upvotes: 0

Mayur Shedage
Mayur Shedage

Reputation: 1037

Basic example of aysnc/await:

console.log('person1: shoe ticket');
console.log('person2: shoe ticket');

const preMovie = async () => {
	const promiseGirlFriendBringingTickets = new Promise((resolve, reject) => {
		setTimeout(() => {
			resolve('ticket');
		}, 3000);
	});

	const addButter  = new Promise((resolve, reject) => {
		resolve('butter');
	});

	const getPopcorn  = new Promise((resolve, reject) => {
		resolve('popcorn');
	});

	let ticket = await promiseGirlFriendBringingTickets;

	console.log(`girlfriend: i have the ${ticket}`);
	console.log('boyfriend: we should go in');
	console.log('girlfriend: no i am hungry');

	let popcorn = await getPopcorn;

	console.log(`boyfriend: I got some ${popcorn}`);
	console.log('boyfriend: we should go in');
	console.log('girlfriend: I need butter on my popcorn');

	let butter = await addButter;

	console.log(`boyfriend: I got some ${butter} on popcorn`);
	console.log('boyfriend: anything else baby ?');
	console.log('girlfriend: Lets go we are getting late');

	return ticket;
}
preMovie().then((m) => console.log(`person3: shoes ${m}`));

console.log('person4: shoe ticket');
console.log('person5: shoe ticket');

Upvotes: 1

Bergi
Bergi

Reputation: 664876

I wouldn't write that extra getIndependentThingOneAndDependentThing function, especially not with async/await. I'd rather go for plain then syntax where it is simpler, and do everything inside getAllData:

const getIndependentThingOne = () => axios.get(`${root}/1`);
const getIndependentThingTwo = () => axios.get(`${root}/2`);
const getDependentThing = ([{id}]) => axios.get(`${root}/3/${id}`);

const getData = ({data}) => data;

async function getAllData() {
  const promiseOne = getIndependentThingOne().then(getData);
  const promiseTwo = getIndependentThingTwo().then(getData);
  const promiseThree = promiseTwo.then(getDependentThing).then(getData);
//                     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

  const [listFromThingOne, listFromThingTwo, listFromThingThree] = await Promise.all([
    promiseOne,
    promiseTwo,
    promiseThree,
  ]);
  console.log('DONE')
  console.log({ listFromThingOne })
  console.log({ listFromThingTwo })
  console.log({ listFromThingThree })
}

getAllData()

(Whether .then(getData) should be moved inside the getThing functions, like async () => (await axios.get(…)).data, comes down to preference)

Upvotes: 1

Jonas Wilms
Jonas Wilms

Reputation: 138397

The "dependendThing" could just take a promise of the thing it deends on:

const getDependentThing = async (dependentThingTwo) => {
  const { data: listFromIndependentThingTwo } = await dependentThingTwo;
 return getDependentThing(
  listFromIndependentThingTwo
 );
}

That way, you can get the results as:

const dependentThing = independentThingTwo();
const [listFromTwo, dependentThing] = await Promise.all([dependentThing, getDependendThing(dependentThing)]);

Upvotes: 0

Related Questions