Build an array of objects where some of the fields need remote data

Viewed 46

I'm trying to build an array of objects where some of the fields need remote data. I wrote the following proof-of-concept and it works but the parallel arrays feel messy. Suggestions for something that keeps the keys and promises together would be appreciated:

async function p1(x) {
  return new Promise((resolve) => {
    setTimeout(() => {
      resolve(`p1-${x}`);
    }, 250);
  });
}

async function p2(x) {
  return new Promise((resolve) => {
    setTimeout( () => {
      resolve(`p2-${x}`);
    }, 250);
  });
}

async function t(n) {
  return { a: await p1(n), b: await p2(n), c: n };
}

async function r() {
  const keys = [];
  const values = [];
  for (let i = 0; i < 2; i++) {
    const key = `k${i}`
    keys.push(key);
    values.push(t(key));
  }
  const x = await Promise.all(values);
  const results = {};
  for (let i = 0; i < keys.length; i++) {
    results[keys[i]] = x[i];
  }
  return results;
}

(async () => {
  const answers = await r();
  console.log('answers', answers);
})().catch((e) => {
  console.log(e);
});
3 Answers

This State Machine also works:

async function p1(x) {
  return new Promise((resolve) => {
    setTimeout(() => {
      resolve(`p1-${x}`);
    }, 250);
  });
}

async function p2(x) {
  return new Promise((resolve) => {
    setTimeout( () => {
      resolve(`p2-${x}`);
    }, 250);
  });
}

async function t(n) {
  return { a: await p1(n), b: await p2(n), c: n };
}

async function r() {
  const results = {}
  for (let i = 0; i < 2; i++) {
   const key = `k${i}`
   results[key] = await t(key)
  }
  return results;
}

(async () => {
  const answers = await r();
  console.log('answers', answers);
})().catch((e) => {
  console.log(e);
});

This one too:

  async function p1(x) {
      return new Promise((resolve) => {
        setTimeout(() => {
          resolve(`p1-${x}`);
        }, 250);
      });
    }

    async function p2(x) {
      return new Promise((resolve) => {
        setTimeout( () => {
          resolve(`p2-${x}`);
        }, 250);
      });
    }

    async function t(n) {
      return { a: await p1(n), b: await p2(n), c: n };
    }

    async function r(questions) {
      return (await Promise.all(
        questions.map(async i => ({[`k${i}`]: await t(`k${i}`)})
        ))).reduce((prev, curr) => ({...prev, ...curr}), {})    
    }

    (async () => {
      const answers = await r(['1', '2']);
      console.log('answers', answers);
    })().catch((e) => {
      console.log(e);
    });

The second one could be refactored better, but I don't know your eventual use-case. It uses no variables and no loops though. That's the approach I'd go with.

No need to cache the keys and values in a separate loop. U can build the results object on the go:

async function p1(x) {
  return new Promise((resolve) => {
    setTimeout(() => {
      resolve(`p1-${x}`);
    }, 250);
  });
}

async function p2(x) {
  return new Promise((resolve) => {
    setTimeout( () => {
      resolve(`p2-${x}`);
    }, 250);
  });
}

async function t(n) {
  return { a: await p1(n), b: await p2(n), c: n };
}

async function r() {
  let results = {};
  for (let i = 0; i < 2; i++) {
    const key = `k${i}`
    results[key] = await t(key);
  }
  return results;
}

(async () => {
  const answers = await r();
  console.log('answers', answers);
})().catch((e) => {
  console.log(e);
});

I think my approach to r would be:

async function r() {
  const results = {};
  const promises = [];
  for (let i = 0; i < 2; i++) {
    const key = `k${i}`
    promises.push(t(key).then(value => results[key] = value));
  }
  await Promise.all(promises);
  return results;
}

Note how results is populated in the loop (asynchronously), we only use promises so we know when everything's done (or, of course, that something failed). This keeps the key and value together in the loop body.

Live copy:

// No need for p1 and p2 to be `asycn` since they don't
// use `await`; I'm guessing your real functions do
// (and don't use `new Promise`)

/*async*/ function p1(x) {
  return new Promise((resolve) => {
    setTimeout(() => {
      resolve(`p1-${x}`);
    }, 250);
  });
}

/*async*/ function p2(x) {
  return new Promise((resolve) => {
    setTimeout( () => {
      resolve(`p2-${x}`);
    }, 250);
  });
}

async function t(n) {
  return { a: await p1(n), b: await p2(n), c: n };
}

async function r() {
  const results = {};
  const promises = [];
  for (let i = 0; i < 2; i++) {
    const key = `k${i}`
    promises.push(t(key).then(value => results[key] = value));
  }
  await Promise.all(promises);
  return results;
}

(async () => {
  const answers = await r();
  console.log('answers', answers);
})().catch((e) => {
  console.log(e);
});
.as-console-wrapper {
    max-height: 100% !important;
}

Related