Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Async function not returning value, but console.log() does: how to do? [duplicate]

I have an es6 class, with an init() method responsible for fetching data, transforming it, then update the class's property this.data with newly transformed data. So far so good. The class itself has another getPostById() method, to just do what it sounds like. Here is the code for the class:

class Posts {
  constructor(url) {
    this.ready = false
    this.data = {}
    this.url = url
  }
  async init() {
      try { 
        let res = await fetch( this.url )
        if (res.ok) {
            let data = await res.json()

          // Do bunch of transformation stuff here

          this.data = data
          this.ready = true
            return data
        }
      } 
      catch (e) { 
         console.log(e)
      }
  }
  getPostById(id){
     return this.data.find( p => p.id === id )
  }
}  

Straightforward, except I have an async/await mechanism in the init() method. Now, this code will work correctly:

let allPosts = new Posts('https://jsonplaceholder.typicode.com/posts')

allPosts.init()
        .then( d => console.log(allPosts.getPostById(4)) )
// resulting Object correctly logged in console

but it only gets printed into the console: How could I use allPosts.getPostById(4) as a return of a function ?

Like:

let myFunc = async () => {
   const postId = 4
   await allPosts.init()  // I need to wait for this to finish before returning

   // This is logging correct value
   console.log( 'logging: ' + JSON.stringify(allPosts.getPostById( postId ), null, 4) )

   // How can I return the RESULT of allPosts.getPostById( postId ) ???
   return allPosts.getPostById( postId )
}

myFunc() returns a Promise but not the final value. I have read several related posts on the subject but they all give example of logging, never returning.

Here is a fiddle that includes two ways of handling init(): using Promise and using async/await. No matter what I try, I can't manage to USE the FINAL VALUE of getPostById(id).

The question of this post is: how can I create a function that will RETURN the VALUE of getPostById(id) ?

EDIT:

A lot of good answers trying to explain what Promises are in regards to the main execution loop. After a lot of videos and other good reads, here is what I understand now:

my function init() correctly returns. However, within the main event loop: it returns a Promise, then it is my job to catch the result of this Promise from within a kinda parallel loop (not a new real thread). In order to catch the result from the parallel loop there are two ways:

  1. use .then( value => doSomethingWithMy(value) )

  2. use let value = await myAsyncFn(). Now here is the foolish hiccup:

await can only be used within an async function :p

thus itself returning a Promise, usable with await which should be embed in an async function, which will be usable with await etc...

This means we cannot really WAIT for a Promise: instead we should catch parallel loop indefinitely: using .then() or async/await.

Thanks for the help !

like image 303
Jona Rodrigues Avatar asked Dec 05 '17 23:12

Jona Rodrigues


People also ask

Does console log work in async?

To our code, it does not make any difference whether console. log is async or not, it does not provide any kind of callback or so; and the values you pass are always referenced and computed at the time you call the function.

How do you make an asynchronous function return a value?

To return values from async functions using async-await from function with JavaScript, we return the resolve value of the promise. const getData = async () => { return await axios.

Does an async function have to return something?

Async functions always return a promise. If the return value of an async function is not explicitly a promise, it will be implicitly wrapped in a promise.

Do async functions automatically return a promise?

Async functions The word “async” before a function means one simple thing: a function always returns a promise. Other values are wrapped in a resolved promise automatically. So, async ensures that the function returns a promise, and wraps non-promises in it.


1 Answers

NOTE: Wherever you use await it has to be inside an async function.

Check out the UPDATED FIDDLE

You need to use await myFunc() to get the value you expect from getPostById because an async function always returns a promise.

This sometimes is very frustrating as the whole chain needs to be converted into async functions but that's the price you pay for converting it to a synchronous code, I guess. I am not sure if that can be avoided but am interested in hearing from people who have more experience on this.

Try out the below code in your console by copying over the functions and then accessing final and await final.

NOTE:

An async function CAN contain an await expression. https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/async_function

There is no rule that is must have await in order to even declare an async function. The example below uses an async function without await just to show that an async function always returns a promise.

const sample = async () => {
  return 100;
}

// sample() WILL RETURN A PROMISE AND NOT 100
// await sample() WILL RETURN 100

const init = async (num) => {
  return new Promise((resolve, reject) => {
    resolve(num);
  });
}

const myFunc = async (num) => {
  const k = await init(num);
  return k;
}

// const final = myFunc();
// final; This returns a promise
// await final; This returns the number you provided to myFunc
like image 147
Nandu Kalidindi Avatar answered Sep 28 '22 09:09

Nandu Kalidindi