简体   繁体   中英

How to use async-await with Promise.all()?

At the moment I am using this code below to get the results of several Promises using async await:

let matchday = await createMatchday(2018, 21, [/*9 matches of matchday*/]);
//Further calculations

async function createMatchday(seasonNr, matchdayNr, matches) {
  let md = new Matchday(seasonNr, matchdayNr, matches);
  await md.getStandings(seasonNr, matchdayNr);
  return md;
}

class Matchday {
  constructor(seasonNr, matchdayNr, matches) {
    this.seasonNr = seasonNr;
    this.matchdayNr = matchdayNr;
    this.matches = matches;
  }

  async getStandings(seasonNr, matchdayNr) {
    let promiseArr = [];
    promiseArr.push(makeHttpRequestTo(`http://externService.com/standings?seasonNr=${seasonNr}&matchdayNr=${matchdayNr}`);
    promiseArr.push(makeHttpRequestTo(`http://externService.com/homestandings?seasonNr=${seasonNr}&matchdayNr=${matchdayNr}`));
    promiseArr.push(makeHttpRequestTo(`http://externService.com/awaystandings?seasonNr=${seasonNr}&matchdayNr=${matchdayNr}`));
    promiseArr.push(makeHttpRequestTo(`http://externService.com/formstandings?seasonNr=${seasonNr}&matchdayNr=${matchdayNr}`));

    let resulArr = await Promise.all(promiseArr);
    this.standings = resultArr[0];
    this.homeStandings = resultArr[1];
    this.awayStandings = resultArr[2];
    this.formStandings = resultArr[3];
  }
}

function makeHttpRequest(url) {
  return new Promise((resolve, reject) => {
    //AJAX httpRequest to url
    resolve(httpRequest.responseText);
  }
}

Is this actually the best way to read the values of several promises where the promises don't need to wait for each other to end but rather work at the same time by using Promise.all() or is there a better way to make eg several httpRequests at the same time because this seems quite repetetive?

Your URLs all follow the same sort of pattern, so you can greatly reduce your code by map ping an array of ['', 'home', 'away', 'form'] to the URLs. Then, map those URLs to Promises through makeHttpRequestTo , and then you can destructure the awaited results into the this. properties:

async getStandings(seasonNr, matchdayNr) {
  const urls = ['', 'home', 'away', 'form']
    .map(str => `http://externService.com/${str}standings?seasonNr=${seasonNr}&matchdayNr=${matchdayNr}`);
  const promiseArr = urls.map(makeHttpRequestTo);
  [
    this.standings,
    this.homeStandings,
    this.awayStandings,
    this.formStandings
  ] = await Promise.all(promiseArr);
}

To populate each property individually rather than waiting for all responses to come back:

async getStandings(seasonNr, matchdayNr) {
  ['', 'home', 'away', 'form']
    .forEach((str) => {
      const url = `http://externService.com/${str}standings?seasonNr=${seasonNr}&matchdayNr=${matchdayNr}`;
      makeHttpRequestTo(url)
        .then((resp) => {
          this[str + 'Standings'] = resp;
        });
    });
}

To answer, No you shouldn't block other XHR or any I/O request which are not dependent on each other. I would have written your function like this;

 const getFavourites = async () => { try { const result = await Promise.resolve("Pizza"); console.log("Favourite food: " + result); } catch (error) { console.log('error getting food'); } try { const result = await Promise.resolve("Monkey"); console.log("Favourite animal: " + result); } catch (error) { console.log('error getting animal'); } try { const result = await Promise.resolve("Green"); console.log("Favourite color: " + result); } catch (error) { console.log('error getting color'); } try { const result = await Promise.resolve("Water"); console.log("Favourite liquid: " + result); } catch (error) { console.log('error getting liquid'); } } getFavourites(); 

This way every async functions will be called at once, and no async action will block the other action.

If you don't want to wait for all requests to complete before continuing the execution flow, you could make the properties of the class be promises:

class Matchday {
  constructor(seasonNr, matchdayNr, matches) {
    this.seasonNr = seasonNr;
    this.matchdayNr = matchdayNr;
    this.matches = matches;
    ['standings', 'homeStandings', 'awayStandings', 'formStandings'].forEach(propertyName => {
      let url = `http://externService.com/${propertyName.toLowerCase()}`
        + `?seasonNr=${seasonNr}&matchdayNr=${matchdayNr}`
      this[propertyName] = makeHttpRequestTo(url)
    });
  }
}

Test using the following snippet

 class Matchday { constructor(seasonNr, matchdayNr, matches) { this.seasonNr = seasonNr; this.matchdayNr = matchdayNr; this.matches = matches; ['standings', 'homeStandings', 'awayStandings', 'formStandings'].forEach(propertyName => { let url = `http://externService.com/${propertyName.toLowerCase()}` + `?seasonNr=${seasonNr}&matchdayNr=${matchdayNr}` this[propertyName] = makeHttpRequestTo(url) }); } } /************************************** * Test harness **************************************/ function makeHttpRequestTo(url) { // Fake an AJAX httpRequest to url const requested_resource = url.match('^.*\\/\\/.*\\/([^?]*)')[1]; const fake_response_data = 'data for ' + url.match('^.*\\/\\/.*\\/(.*)$')[1]; let delay = 0; let response = ''; switch (requested_resource) { // To make it interesting, let's give the 'standings' resource // a much faster response time case 'standings': delay = 250; break; case 'homestandings': delay = 2000; break; case 'awaystandings': delay = 3000; break; case 'formstandings': delay = 4000; // <== Longest request is 4 seconds break; default: throw (util.format('Unexpected requested_resource: %s', requested_resource)); } return new Promise((resolve, reject) => { setTimeout(() => resolve(fake_response_data), delay); }); } async function testAccessingAllProperties() { const testId = "Test accessing all properties"; console.log('\\n%s', testId); console.time(testId) let md = new Matchday(2018, 21, []); console.log(await md.standings); console.log(await md.homeStandings); console.log(await md.awayStandings); console.log(await md.formStandings); console.timeEnd(testId) } async function testAccessingOnlyOneProperty() { const testId = `Test accessing only one property`; console.log('\\n%s', testId); console.time(testId) let md = new Matchday(2018, 21, []); console.log(await md.standings); console.timeEnd(testId) } async function all_tests() { await testAccessingAllProperties(); await testAccessingOnlyOneProperty(); } all_tests(); 

Conclusion

The above snippet shows that the execution time is not penalized by properties that are not accessed. And execution time of accessing all properties is no worse than using promise.all .

You'd just need to remember to use await when accessing those properties.

To create a Promise you need to call new Promise((resolve, reject) => { return "Pizza"; })

You're doing it the right way

If you want you can shorten the code by using an array (and its functions like map , etc...), but it won't improve its performance

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM