Async function question

Beginner here.

The following function:

async function ATCall(url){
    let ATRequest = new Request(url)
    let ATResponse = await ATRequest.loadJSON()
    let response = ATResponse.records
    return response
}

returns an empty object. If I console.log(response) before the return, it does log the data, but this console log appears after the console.log(ATCall(aturl)) that calls the function logs an empty object {}.

So - I’m surmising that the return is being sent before the data arrives. Which I thought I was addressing with async/await.

This is a function I intend to use to call Airtable to retrieve records from two or more tables within a script. So I though it would be best to try to make the call from a function to which I could feed the url for the appropriate table. Just not sure why the return is occurring before the promise is fulfilled?

By the way - in an attempt to isolate the issue, I created a script that essentially only contains this function, my Airtable url and auth, and the console.log to call it. I wanted to see it work in isolation. There isn’t anything else in the script currently except comments.

Maybe you are missing the await to this function call?

console.log(await ATCall("some://url"))

If this is the case, the calling code doesn’t wait for the returned Promise to be fullfilled and logs the Promise object instead of the request response.

Thank you so much. That worked.

So to clarify, do I need the await on line 3 of the function also? Or is it more proper to put the await on the function call?

You need it on every function that returns a Promise and you want to wait until this Promise is fulfilled. Functions, defined with the async keyword and using await, implicitly return a promise too.

Thank you very much.

1 Like

In my further effort to pull data from Airtable, I ran into another confusing issue.

In the following code snippet, the “Manager List” portion runs fine.

The “People List portion” ends up with peopleList as undefined. The console.log(response) just above the return from ATCall appears to log valid JSON containing over 300 records. There is a portion of the function to deal with pagination.

I have stared at this for 3 hours. I can’t see the solution. I don’t understand why responses is fine but peopleList ends up undefined.

My theories have been that the await isn’t functioning (although the console logs appear to be timed correctly.) Also, that the .concat is perhaps not implemented correctly resulting in the JSON being invalid (although it appears to be valid to me, and it appears correct in Jayson.)

Any help would be appreciated.

Thank you.

async function ATCall(url, offset){
    if (!offset)
    {
    ATRequest = new Request(url)
    } else {
    ATRequest = new Request(url + "&offset=" + offset)
    }
    let ATResponse = await ATRequest.loadString()
    let raw = JSON.parse(ATResponse)
    if (!response){
        response = raw.records
    } else {
        response = response.concat(raw.records)
    }
    if (!raw.offset){
        console.log(response)
        return response        
    } else {
        await ATCall(url, raw.offset)
    }
}
let managerList = await ATCall(managerurl)
let listtype = "Manager"
//saveList(managerList,listtype)
let peopleList = await ATCall(peopleurl)
listtype = "Team"
//saveList(peopleList,listtype)
//console.log(managerList)
console.log(peopleList)

It’s a little difficult to say what goes wrong, because there is some missing information. I assume some things, but please correct me if I’m wrong.

Assumptions:

  • the request returns JSON of the form {"records": [], "offset": 42}
  • if data is missing from the request, still valid JSON is returned, but not all records
  • the variable response is declared globally before you call the function the first time (just declared, not initialized, so you haven’t assigned any value yet)

The only thing I can say for sure in this case that you don’t reset the response variable before requesting the people list. It will have some impact, but I can’t say how much.

I would change the function a bit, to eliminate the global variable and possibly your issue:

async function ATCall(url){
    // initialize variables
    let records = []
    let offset = null;
    // send the request at least once => use do ... while
    do {
        if (!offset) {
            ATRequest = new Request(url)
        } else {
            ATRequest = new Request(url + "&offset=" + offset)
        }
        // there is this method that directly returns an object from the JSON so you don't have to convert it by yourself
        let raw = await ATRequest.loadJSON()
        records = records.concat(raw.records)
        offset = raw.offset
    // repeat as long as the data isn't complete
    } while(offset)
    return records
}

Thank you so much. I learned a lot from this answer, and yes it works perfectly. I had just read a chapter on recursive functions, so I was trying to apply that, but I see how much more appropriate a do while loop is. It solved problems like variable scope that I simply hadn’t handled properly.

Interestingly, to test your theory about the response variable, I set response to null just prior to the function call, and it still returned undefined to the variable. (I was just trying to learn everything I could from the interaction.) I’m the type of person that would love to learn “why”, but in this case I’ll just accept the answer. :grinning:

However, I really appreciate you taking the time to look at it and help me. Thanks again.

You’re welcome!

Well, to really answer that, you would need to debug the code with a step debugger, but sadly this isn’t available in Scriptable :pensive: