8

I have a slew of async functions I'm using and I'm having a weird issue.

My code, working, looks like:

async mainAsyncFunc (metadata) {
  let files = metadata.map(data => this.anotherAsyncFunc(data.url));
  return Promise.all(files);
}

anotherAsyncFunc function looks like:

  async anotherAsyncFunc (url) {
    return await axios({
      url,
    }).then(res => res.data)
      .catch(err => {
      throw err;
    });
  }

My issue comes when I try to append more data to what the first function (mainAsyncFunc) returns. My thoughts are to do that in the map, naturally, and when all is said and done, modified it looks like:

async mainAsyncFunc (metadata) {
    files = metadata.map(data => {
        return new Promise((resolve) => {
          let file = this.anotherAsyncFunc(data.download_url);
          let fileName = data.name;
          resolve({
            file,
            fileName
          });
        });
      });
    return Promise.all(files);
}

If it's not clear, I'm getting the file itself like normal, and appending a fileName to it, then resolving that object back.

For some reason, this is returning a pending Promise, whereas I would expect it to wait for them to be fulfilled and then returned as a complete file and name in an object. Any help understanding what I'm doing wrong would be greatly appreciated.

Felix Kling
  • 795,719
  • 175
  • 1,089
  • 1,143
Zach
  • 4,555
  • 9
  • 31
  • 52
  • 2
    `async` functions return promises. Nor can you turn a Promise-wrapped value into an ordinary value, you will always have to `await` or call `.then`. Why did you expect to get an array? – Jared Smith Apr 03 '17 at 20:56
  • @JaredSmith I use `Promise.all()` because I'm mapping over a dataset and returning the result of async function for each item. Thus creating an array. However, I just solved my own issue and am posting a solution now – Zach Apr 03 '17 at 20:58
  • `Promise.all` returns a Promise. Again, you *cannot 'unwrap' a promise*. You can only `.then` it. – Jared Smith Apr 03 '17 at 20:58
  • @JaredSmith I can assure you my working code is returning an array of fulfilled objects :). If it helps you understand it better, I call this function like: `FileRetriever.mainAsyncFunc(metadata).then(contents => ...)`, and with my new working code `contents` is an array. – Zach Apr 03 '17 at 21:01
  • No, its not, your code does not work the way you seem to think it does. `await` is sugar over `.then`. You cannot ever unwrap a promise. Its a logical impossibility in the general case, as *there is no way* to tell if a promise is resolved or not. – Jared Smith Apr 03 '17 at 21:06
  • Handy tip: Any time you find yourself using `new Promise` when the values you're working with are already promises, it usually means you've got something wrong. – JLRishe Apr 03 '17 at 21:12

2 Answers2

13

It looks like you've solved your issue, just as a bit of a pointer, you can further simplify your code as follows:

async anotherAsyncFunc (url) {
    return (await axios({ url })).data;
}

async mainAsyncFunc (metadata) {
    let files = metadata.map(async data => ({
        file: await this.anotherAsyncFunc(data.download_url),
        fileName: data.name
    }));

    return Promise.all(files);
}
JLRishe
  • 99,490
  • 19
  • 131
  • 169
4

Just solved the issue:

  files = metadata.map(async (data) => {
    let file = await this.anotherAsyncFunction(data.download_url);
    let fileName = data.name;
    return {
      file,
      fileName
    };
  });

I needed to wrap the anonymous function in async so I could use await inside of it. Hopefully this helps anyone else with a similar problem!

Zach
  • 4,555
  • 9
  • 31
  • 52