0

I am in a asynchronous function, trying to return the data from a callback function:

async function getData(){
    const client = new google.auth.JWT(
        keys.client_email,
        null,
        keys.private_key,
        ['https://www.googleapis.com/auth/spreadsheets']    
    )

    let returnData
    const finalData = (data) => {
        returnData = data
    }

    async function gsrun(client) {
        const gsapi = google.sheets({version:'v4', auth: client})

        const options = {
            spreadsheetId: '1d3ZiP1I9jJ2ddlD1Hx2ylWn1VFD_5lYQ9Ps9e9gEqI',
            range: 'Sheet1!A1:H5'
        }

        const data = await gsapi.spreadsheets.values.get(options)

        return data.data.values
    }

    client.authorize( async (err, tokens)=> {
        if(err) return console.log(err)
        let data = await gsrun(client)
        finalData(data)
    })

    return returnData
}

And in the console I get: Promise { undefined }. How should I await that promise to resolve or to await the data?

Felix Kling
  • 705,106
  • 160
  • 1,004
  • 1,072
  • Does this answer your question? [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) – CertainPerformance Mar 10 '20 at 08:18
  • and https://stackoverflow.com/questions/22519784/how-do-i-convert-an-existing-callback-api-to-promises and https://stackoverflow.com/questions/23667086/why-is-my-variable-unaltered-after-i-modify-it-inside-of-a-function-asynchron – CertainPerformance Mar 10 '20 at 08:18
  • Can you show us your `gsrun()` function? – R.Sarkar Mar 10 '20 at 08:25
  • I followed the documentation but is not what I need. It says nothing about returning the data from a async callback function. –  Mar 10 '20 at 08:47
  • I just added the hole function –  Mar 10 '20 at 08:52

2 Answers2

0

Your client.authorize is already awaiting gsrun() function to return a resolve (I am not sure how the rejection from gsrun() is handled but I suggest you use a .then() to handle resolve/reject if not implement a try,catch statement).

Another issue is that following code will still run despite you awaiting gsrun(client)

return returnData

because it is not inside the finalData() function. I suggest you only execute it is set.

Here's to show that returnData is returned before data is assigned to it

function returnPromise() {
    return new Promise((resolve, reject) => {
      setTimeout(()=>{
        resolve('Success')
      },2000)
      // reject('err')
    })
}

async function test(){
  returnPromise().then(
    resolve=>{console.log(resolve)},
    reject=>{console.log(reject)}
  )
}

test()
console.log('I COME HERE FIRST') // Executed first before promise is returned

Here's a simple example of how you could implement rejection handling(for reference only)

function gsrun() {
    return new Promise((resolve, reject) => {
      //resolve('Success')
       reject('err')
    })
}

async function authorizeCallback(){
    gsrun().then(
    resolve=>{finalData(resolve)},
    reject=>{console.log(reject)} // do error handling
  )
}
CodeCodey
  • 101
  • 4
  • I know the order the functions execute, but I don't know how to solve this issue. What should I await or what should I change? –  Mar 10 '20 at 09:02
  • Think Felix King did a better job at scoping the problem clearly. Nonetheless to answer your question, by relocating your "return returnData;" into finalData() function actually solves the problem. This is so that once your gsrun() returns the Promise, finalData() is executed. Oh and I did not realize you edited your post for the full code. Sorry for posting an answer with reference to the previous code. – CodeCodey Mar 10 '20 at 09:24
  • Thanks for the explanations, I really appreciate it! –  Mar 10 '20 at 13:22
0

You are still trying to return before the data was assigned. You are not "waiting" until client.authorize has called the callback because you can't. You can only "return data from a callback" if the callback is called synchronously. But that's not the case here. It doesn't matter that you declared the callback as async, what matters is how/when the caller (i.e. client.authorize) calls the callback.

Wrap the client.authorize in a promise and await that in your getData function.

async function gsrun(client) {
  const gsapi = google.sheets({
    version: 'v4',
    auth: client
  })

  const options = {
    spreadsheetId: '1d3ZiP1I9jJ2ddlD1Hx2ylWn1VFD_5lYQ9Ps9e9gEqI',
    range: 'Sheet1!A1:H5'
  }

  const data = await gsapi.spreadsheets.values.get(options)

  return data.data.values
}


function authorize(client) {
  return new Promise((resolve, reject) => {
    client.authorize((err, tokens) => {
      if (err) {
        reject(err);
      } else {
        resolve(tokens);
      }
    });
  });
}


async function getData() {
  const client = new google.auth.JWT(
    keys.client_email,
    null,
    keys.private_key, ['https://www.googleapis.com/auth/spreadsheets']
  )

  await authorize(client);
  return await gsrun(client);
}

Here authorize will throw an error if the authentication failed and return the tokens if it is successful.


I wrote a post about callbacks and data. While it doesn't go into promises, maybe it can help your understanding of callbacks.

Felix Kling
  • 705,106
  • 160
  • 1,004
  • 1,072