Shaun Vermaak
Shaun Vermaak

Reputation: 311

Async function call not complete in callback

I am trying to work with some async functions but my code, for example this.saveAsCSV(lists,"lists.csv"); seems to continue before the function completes. This is not exactly what I am doing but it is the shortened version that shows my issue. console.log(lists) has the correct data but I assume it is evaluated after the call

   public render(): void {
    (async () => {  
      await this.GetListsBySiteID("6a368204-0534-4ffb-8014-157524ca9d50").then(lists => {
        console.log(lists);
        this.saveAsCSV(lists,"lists.csv");      
      });
    })();
  }

  async GetListsBySiteID(id:string):Promise<string[]>
  {
    let lists1:string[] = [];

    await pnp.sp.site.openWebById(id).then(result => 
    {
      result.web.lists.get().then(lists => 
      {
        lists.forEach(list => {
          lists1.push(list.Title);
        });      
      })
    });

    return lists;
  }

How do I correct this?

Upvotes: 0

Views: 643

Answers (3)

palaѕн
palaѕн

Reputation: 73906

Here is a full async/await solution to this issue. Using await on result.web.lists.get() will wait for the process to finish first and then when resolved, finally giving you the correct lists

public render(): void {
  (async() => {
    const lists = await this.GetListsBySiteID("6a368204-0534-4ffb-8014-157524ca9d50")
    console.log(lists);
    this.saveAsCSV(lists, "lists.csv");
  })();
}

async GetListsBySiteID(id:string):Promise<string[]> {
  let lists1: string[] = [];
  const result = await pnp.sp.site.openWebById(id)
  const lists = await result.web.lists.get()
  lists1 = lists.map(x => x.Title);
  return lists1;
}

Please note that in your post, you are returning lists at the end inside GetListsBySiteID and that variable is not defined inside the function. So, if you want to return lists1 instead simply replace return lists; with return lists1;.

Upvotes: 1

Ariel Perez
Ariel Perez

Reputation: 519

The code is not tested, but this is the idea:

  1. Return a promise in the GetListsBySiteID function
  2. resolve the promise after you have the lists value populated
GetListsBySiteID(id:string):Promise<string[]> {
  return new Promise((resolve, reject) => {
    try {
      let lists:string[] = [];
      pnp.sp.site.openWebById(id).then(result => 
      {
        result.web.lists.get().then(lists => 
        {
          lists.forEach(list => {
            lists.push(list.Title);
          });    

          resolve(lists);
        })
      });
    } catch (ex) {
      reject(ex);
    }
  );
}

Upvotes: 1

Sebastian Kaczmarek
Sebastian Kaczmarek

Reputation: 8515

It's because in your GetListsBySiteID() function you have this code:

await pnp.sp.site.openWebById(id).then(result => 
{
  result.web.lists.get().then(lists => 
  {
    lists.forEach(list => {
      lists.push(list.Title);
    });      
  })
});

So what happens here is JavaScript is awaiting for the pnp.sp.site.openWebById(id) Promise to complete before continuing. You have attached the .then() callback so it's being awaited too. Now the problem is in your .then() callback:

You are calling result.web.lists.get().then() but this Promise is not being awaited for. The solution is to add return keyword, like this:

await pnp.sp.site.openWebById(id).then(result => 
{
  return result.web.lists.get().then(lists =>  // <-- here I added return
  {
    lists.forEach(list => {
      lists.push(list.Title);
    });      
  })
});

So now the await will cause JavaScript to wait for the nested Promise also.

Upvotes: 1

Related Questions