Search code examples
javascriptarrayspromisees6-promise

How do I use Promise.all() to defer returning an array that's being built inside the function?


Below I'm attempting to assign a value, an array, to a local variable. I'm calling a function that returns an array to get that value. The problem is that the Promises in the 2nd function aren't resolved until after the array has been returned to the caller. I've tried using Promise.all() on retArray but it never works for me. When I console.log() out my someobject object the arrOfTitles field never prints out because the call to SkywalkerTitles() returns an empty array.

You can run the code here.

So how do I get someObject.arrOfTitles to get the array of titles from SkywalkerTitles()?

function SkywalkerTitles(){
  let retArray = [];

  fetch('https://swapi.co/api/people/')
  .then(function(response){
    response.json()
    .then(function(result){
      return result.results[0];
    })
    .then(function(result){
       result.films.forEach(function(film){
        fetch(film)
        .then(function(response){
          response.json().then(function(result){
            console.log(result.title);
            retArray.push(result.title);
          });
        });
      })
      .catch(function(error){
        console.log(error)
      });
    });
  })
  .catch(function(error){
    console.log(error)
  });

}

function UseReturnedArray() {
  let someObject = { aThing: '', anotherThing: '', arrOfTitles: null };

  someObject.aThing = 'Thing One';
  someObject.anotherThing = 'Thing Two';
  someObject.arrOfTitles = SkywalkerTitles();

  console.log('Object With Returned Array:\n\n', JSON.stringify(someObject, null, 2));
}

UseReturnedArray();

Solution

  • As much as @Svenskunganka answer is complete and worthy, alternative if your environment doesn't yet support async/await AND you don't want to use a transpiler - seeing as you're already partially familiar with Promises, this code shouldn't look as foreign :p

    Your main problem is that SkywalkerTitles doesn't actually return anything (you claim it returns an empty array, it actually has no return statement, therefore the returned value is undefined

    I've also removed the .catch code, because where you had it would actually cause issues, in that your code handles rejections, yet further down the chain the code would expect that the data was actually valid! Only a single catch almost always ever needed

    function SkywalkerTitles() {
        return fetch('https://swapi.co/api/people/').then(function (response) {
            return response.json();
        }).then(function (result) {
            return result.results[0];
        }).then(function (result) {
            return Promise.all(result.films.map(function (film) {
                return fetch(film).then(function (response) {
                    return response.json();
                }).then(function (result) {
                    return result.title;
                });
            }));
        });
    }
    
    function UseReturnedArray() {
        SkywalkerTitles().then(function (arrOfTitles) {
            var someObject = {
                aThing: '',
                anotherThing: '',
                arrOfTitles: arrOfTitles
            };
            console.log('Object With Returned Array:\n\n', JSON.stringify(someObject, null, 2));
        }).catch(function(reason) {
            console.log(reason);
        });
    }
    UseReturnedArray();
    

    Note how the promise chain is flattened compared to yours, and use of Array#map instead of Array#forEach + Array#push