Drun
Drun

Reputation: 619

Async function inside an async function returns undefined value

I'm working in a JS class and I can't figure out the reason why colors.ColorCheck(0).ColorCode and colors.ColorCheck(0).Color return undefined values.

As you can see, "0" is passed as an argument of the function ColorCheck and is used inside the functions this.ColorCode and this.Color.

I'm pretty sure I'm missing something in declaring the two functions (this.ColorCode and this.Color) inside the main function (ColorCheck), but I can't see what.

I'm a newbie in JS, so I wouldn't be surprised if the solution seems evident for you guys.

class Colors {

    constructor(){}

    async ColorCheck(ID){
        this.ColorCode = (async()=>{
            this.info = await axios.get ('https://.../${ID}.json')
            this.CodeC = this.info.map(t=>t.colorcode)
            return this.CodeC
        })
        this.Color = (async()=>{
            this.info = await axios.get ('https://.../${ID}.json')
            this.C = this.info.map(t=>t.color)
            return this.C
        })
    }
}

const colors = new Colors()
colors.ColorCheck(0).ColorCode
colors.ColorCheck(0).Color

Upvotes: 1

Views: 879

Answers (3)

Danziger
Danziger

Reputation: 21171

Your ColorCheck function is not returning anything, so when calling it as colors.ColorCheck(0), that's returning undefined.

I think rather than setting this.ColorCode and this.Color, with this referring to the Color instance, what you actually want is to return an object with two keys, ColorCode and Color, whose values are two curried async functions that will make a GET request with the provided id when called.

That would probably look something like this:

// Just mocking axios for this example:

const axios = {
  get: (url) => new Promise((resolve, reject) => {
    console.log(`Making a request to ${ url }...`);
    
    setTimeout(() => {
      resolve([{
        colorCode: '#F00',
        color: 'red',
      }, {
        colorCode: '#0F0',
        color: 'green',
      }, {
        colorCode: '#00F',
        color: 'blue',
      }]);
    }, 2000);
  }),
};

class Colors {
  constructor() { }
  
  ColorCode(id) {
    return {
      ColorCode: (async () => {
        const info = await axios.get(`https://.../${ id }.json`);
        
        return info.map(t => t.colorCode);
      }),

      Color: (async () => {
        const info = await axios.get(`https://.../${ id }.json`);
        
        return info.map(t => t.color);
      }),
    };
  }
}

const colors = new Colors();
const returnedValue = colors.ColorCode(0);

(async () => {
  console.log((await returnedValue.ColorCode()).join(', '));
  console.log((await returnedValue.Color()).join(', '));
})();
.as-console-wrapper {
  max-height: none !important;
}

You could also move the axios call to the ColorCode function (without making it async) so that you don't make an additional request every time any of the two returned functions are called:

// Just mocking axios for this example:

const axios = {
  get: (url) => new Promise((resolve, reject) => {
    console.log(`Making a request to ${ url }...`);
    
    setTimeout(() => {
      resolve([{
        colorCode: '#F00',
        color: 'red',
      }, {
        colorCode: '#0F0',
        color: 'green',
      }, {
        colorCode: '#00F',
        color: 'blue',
      }]);
    }, 2000);
  }),
};

class Colors {
  constructor() { }
  
  ColorCode(id) {  
    // Note there no `await` here:
    const infoPromise = axios.get(`https://.../${ id }.json`);
    
    return {
      ColorCode: (async () => {
        return (await infoPromise).map(t => t.colorCode);
      }),

      Color: (async () => {
        return (await infoPromise).map(t => t.color);
      }),
    };
  }
}

const colors = new Colors();
const returnedValue = colors.ColorCode(0);

(async () => {
  console.log((await returnedValue.ColorCode()).join(', '));
  console.log((await returnedValue.Color()).join(', '));
})();
.as-console-wrapper {
  max-height: none !important;
}

Upvotes: 2

shuk
shuk

Reputation: 1823

welcome! just remember to await or then the host async function as well

example:

async function a() {
  return await http.get(...)
}
a() // undefined (promise awaiting resolve)
await a(); // expected value
a().then(value => console.log(value)) // expected value

in case of function a(), you can just return the async task itself, which you will have to resolve later anyway:

function a() {
  return http.get(...)
}

a() // undefined (promise awaiting resolve)
await a() // expected value
a().then(...) // expected value

also: - you assigned this.ColorCode to be a(n async) function, which means you will have to call it in order to get value retuned to it:

await Colors.ColorCheck('0'); // Colors.ColorCode and Colors.Color are now being assigned
await Colors.ColorCode(); // this.CodeC is being assigned to the class Colors + returned. which means from now on Colors.CodeC is also accessible
await Colors.Color() // assigns Colors.C a value + returns it. from now on Colors.C is also accessible

note that Colors.info is also assigned and overridden each time you call any of the functions (Colors.ColorCode / Colors.Color)

  • the this keyword is most likely misused: refer to this article in MDN. currently your values are stored within the class Colors context, instead of probably just storing them in the function's context, using const. e.g:
const info = await axios...
const c = info.map(...);
return c;

Upvotes: 1

Ricardo Rocha
Ricardo Rocha

Reputation: 16266

You want to use string literals, but you are defining as a normal string:

this.info = await axios.get ('https://.../${ID}.json');

You should change to this:

this.info = await axios.get (`https://.../${ID}.json`);

Upvotes: -1

Related Questions