1

I have the following function which essentially calls three other promise based functions. The promises all seem to be woking, however I can't get the function to return the tableData array.

Any pointers would be very helpful.

function getALLBooks() {

  tableData = []

  getAPIKey(creds)
  .then(apikey => {
  //  console.log(apikey)
    getBooks(apikey).then(books => {
    //  console.log(books)
      books.forEach(function (value) {
     //  console.log(value.Guid);
       getBook(apikey, value.Guid).then(book => {
     //    console.log(apikey)
     //    console.log(book)
          console.log(book.Name)
         tableData.push({
          "name": book.Name
         })
       })
       
     });
     return tableData
    })
  })
  .catch(error => {
    console.log(error)
  })
}

getALLBooks()
user1513388
  • 7,165
  • 14
  • 69
  • 111
  • this is an anti-pattern you should avoid this, rather consider chaining promises or using async/await – laxman Sep 04 '19 at 13:07
  • Hmm, I thought I was actually chaining my promises! – user1513388 Sep 04 '19 at 13:08
  • 1
    Returning `tableData` is useless unless it is in a `.then` that happens after everything is resolved. `async`/`await` code is much easier to write and understand once you start to use it. Chained `.then`s can get difficult to make sure they are "right". – crashmstr Sep 04 '19 at 13:10
  • @user1513388 What about getting an array of promises and then using `Promise.all()` to resolve all at once? https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/all – Bear Sep 04 '19 at 13:12

3 Answers3

1

I would suggest to chain the promises:

  getAPIKey(creds).then(apikey => {
     return getBooks(apikey).then(books => {
        return Promise.all(books.map(value => getBook(apikey, value.Guid)));
     });
  }).then(tableData => {
     // do something wit array of data
  });
jcubic
  • 61,973
  • 54
  • 229
  • 402
  • Exactly, that what I suggested in comment under the question. :) – Bear Sep 04 '19 at 13:13
  • So this seems to do the trick for me, except when I wrap it in a function I can't seem to get the tableData to return. – user1513388 Sep 04 '19 at 13:53
  • @user1513388 check [How do I return the response from an asynchronous call?](https://stackoverflow.com/questions/14220321/how-do-i-return-the-response-from-an-asynchronous-call/14220323#14220323) – jcubic Sep 04 '19 at 15:00
  • So I've re-written my code as per the suggestions from @jcubic. However, I need to wrap the code into a function that can be called when I need the tabledata. However, can't get the function to return my tabledata, it always returns undefined. – user1513388 Sep 04 '19 at 15:01
  • @user1513388 read the question I've linked this was asked lot of times, lot of question that are asking same thing. You can't return table if you add item to table asynchronously, instead of return array you need async function the same as your API calls. – jcubic Sep 04 '19 at 15:04
1

first of all, either use async/await or chain your promises, async/await is syntactically simpler.

async function getALLBooks() {

  tableData = [];
  try{
    const apikey  = await getAPIKey(creds);
    const books = await getBooks(apikey);
    const booksPromise = [];
    books.forEach(function (value) {
      booksProm.push(getBook(apikey, value.Guid));
    }
    const tableData = await booksPromise;
  }catch(err){
    console.log("error thrown by first await");
    throw err;
  }
}

getALLBooks.then(tableData => {
  console.log(tableData)
}).catch(err => {
  console.log(err);
})

Reason why your code snippet is not providing desired result,

function getALLBooks() {

  tableData = []

  getAPIKey(creds)
  .then(apikey => {
   /* At this point, when your control reaches here the following getBooks() function is called asynchronously and hence tableData ie empty array is returned as fulfilled value */
   getBooks(apikey).then(books => {
    //  console.log(books)
      books.forEach(function (value) {
     //  console.log(value.Guid);
       getBook(apikey, value.Guid).then(book => {
     //    console.log(apikey)
     //    console.log(book)
          console.log(book.Name)
         tableData.push({
          "name": book.Name
         })
       })

     });
       /*this is returned before your getBooks() operation actually starts.*/
      return tableData
    })
  })
  .catch(error => {
    console.log(error)
  })
}
laxman
  • 1,781
  • 4
  • 14
  • 32
1
async function getALLBooks() {
    const apikey = await getAPIKey(creds);
    const books = await getBooks(apikey);

    const promises = books.map(book => {
        return getBook(apikey, book.Guid)
    });

    const results = await Promise.all(promises);
    return results.map(book => ({ name: book.Name }))
}

getALLBooks().then(books => console.log(books));
Mike
  • 801
  • 4
  • 7