0

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();
Rahul Sharma
  • 2,867
  • 2
  • 27
  • 40
SethD
  • 3
  • 2
  • 1
    If the promise correctly resolves to the array (which it doesn't seem to at the moment) and if you return the promise from the function (which you are also not doing): `SkywalkerTitles().then(result => someObject.arrOfTitles = result)`. You might want to have a look at [How do I return the response from an asynchronous call?](https://stackoverflow.com/q/14220321/218196) – Felix Kling Sep 26 '17 at 04:30
  • `SkywalkerTitles` doesn't return anything ... so `someObject.arrOfTitles` will be `undefined` ... also, as `SkywalkerTitles` has an asynchronous component, the only thing `someObject.arrOfTitles` could ever be is a promise object – Jaromanda X Sep 26 '17 at 05:10

2 Answers2

1

You should take advantage of async/await, which could really clean up your code quite a bit and make it more understandable:

async function SkywalkerTitles () {
  let character = await fetch('https://swapi.co/api/people/').then(res => res.json()).then(res => res.results[0])
  return await Promise.all(character.films.map(async (film) => {
    return await fetch(film).then(res => res.json()).then(res => res.title)
  }))
}

async function UseRetrunedArray () {
  try {
    let someObject = {
      aThing: '',
      anotherThing: '',
      arrOfTitles: await SkywalkerTitles()
    }

    console.log(someObject)
  } catch (e) {
    console.error(e)
  }
}

UseRetrunedArray()

See https://repl.it/Lc2f/2

If this looks alien to you, I suggest you read a bit into how async/await and Promise work together.

Sven
  • 5,155
  • 29
  • 53
  • Thank you @Svenskunganka for the suggestion. I'll look into how async/await work with Promise. This syntax may take a bit for me to wrap my head around as I'm very new to working with Promises. Always appreciate an opportunity to learn though so thank you! – SethD Sep 26 '17 at 11:54
1

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

Jaromanda X
  • 53,868
  • 5
  • 73
  • 87
  • That old school nesting.. :x But 100% good example about how Promise works. – Arup Rakshit Sep 26 '17 at 06:38
  • it's actually "babeljs" nesting :p – Jaromanda X Sep 26 '17 at 07:02
  • Thank you @JaromandaX .. not only does this work but your explanation helps me understand what is happening with the Promises. I left the return off of SkywalkerTitles() on purpose because I thought it was somehow connected to or reliant upon the Promise.all() I thought I needed so I left both Promise.all() and the return out of the sample code. Thanks again for the great answer. – SethD Sep 26 '17 at 11:51