0

I was trying out some things with async/await and for some reason my function runs before my await is finished, can someone tell me why?

(function() {
  var posts = [{
      title: "Post 1",
      body: "This is post 1"
    },
    {
      title: "Post 2",
      body: "This is post 2"
    },
    {
      title: "Post 3",
      body: "This is post 3"
    }
  ];

  function GetPosts() {
    setTimeout(() => {
      let output = '';
      posts.forEach((post, index) => {
        output += `<li>${post.title}</li>`;
      });

      document.body.innerHTML = output;
    }, 1000);
  }


  function CreatePost(post) {
    setTimeout(() => {
      posts.push(post);
    }, 4000);
  }

  async function init() {
    await CreatePost({
      title: "Post 4",
      body: "This is post 4"
    });

    GetPosts();
  }

  init();

})();
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
Chris
  • 2,953
  • 10
  • 48
  • 118
  • Are you talking about `GetPosts()`? If so, you are calling it outside of the `await` section, so it runs right away. – Scott Marcus Jul 20 '20 at 14:19
  • 2
    You need to return [Promise](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise) from `CreatePost` – Nikita Madeev Jul 20 '20 at 14:20
  • 1
    https://stackoverflow.com/questions/33289726/combination-of-async-function-await-settimeout – epascarello Jul 20 '20 at 14:21
  • 1
    The function `CreatePost()` is not asynchronous. It does return a [`Promise`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Using_promises). Using `await` with it does not have any effect. – axiac Jul 20 '20 at 14:23
  • @axiac, so using timeouts has no affect on async/await? – Chris Jul 20 '20 at 14:24
  • This may be a helpful resource, with many examples.: https://javascript.info/async-await – nihiser Jul 20 '20 at 14:25
  • @Chris no setTimeout has nothing to do with async/await, You need to return it inside promise chain to take it into account. – Jacob Sobus Jul 20 '20 at 14:26
  • 1
    The timeout does what it is supposed to do. The issue is that `CreatePost()` postpones some code to be executed later and does not provide any way to track its execution of know when it is completed. – axiac Jul 20 '20 at 14:27

4 Answers4

0

The function CreatePost() is not async, it does not return a Promise. This is why putting await in front of its call does not have any effect.

In order to make it async let it create and return a Promise:

  async function CreatePost(post) {
    return new Promise((resolve) => {
      setTimeout(() => {
        posts.push(post);
        resolve();
      }, 4000);
    });
  }

The same for getPosts().

axiac
  • 68,258
  • 9
  • 99
  • 134
0

Since setTimeout doesn't return any promise. It'll not wait till CreatePost function gets executed. So wrapping the same thing inside a Promise will help to give the output that you are looking for.

(function() {
  var posts = [{
      title: "Post 1",
      body: "This is post 1"
    },
    {
      title: "Post 2",
      body: "This is post 2"
    },
    {
      title: "Post 3",
      body: "This is post 3"
    }
  ];

  function GetPosts() {
    setTimeout(() => {
      let output = '';
      posts.forEach((post, index) => {
        output += `<li>${post.title}</li>`;
      });

      document.body.innerHTML = output;
    }, 1000);
  }

//Wrap this with a new promise
  function CreatePost(post) {
    return new Promise((resolve, reject) => {
      setTimeout(() => {
        posts.push(post);
        resolve();
      }, 3000);
    })

  }

  async function init() {
    await CreatePost({
      title: "Post 4",
      body: "This is post 4"
    });

    GetPosts();
  }

  init();

})();
Nithish
  • 5,393
  • 2
  • 9
  • 24
0

If you want to use async, await is promise

(function () {
  var posts = [{
    title: "Post 1",
    body: "This is post 1"
  },
  {
    title: "Post 2",
    body: "This is post 2"
  },
  {
    title: "Post 3",
    body: "This is post 3"
  }
  ];

  function GetPosts() {
    return new Promise(resolve => {
      setTimeout(() => {
        let output = '';
        posts.forEach((post, index) => {
          output += `<li>${post.title}</li>`;
          resolve('success');
        });
        document.body.innerHTML = output;
      }, 1000);
    })
  }

  function CreatePost(post) {
    return new Promise((resolve) => {
      setTimeout(() => {
        posts.push(post);
        resolve('success')
      }, 4000);
    })
  }

  async function init() {
    const a = await CreatePost({
      title: "Post 4",
      body: "This is post 4"
    });
    const b = await GetPosts();
    return b
  }
  init();
})();
iopzhu
  • 149
  • 5
0
function CreatePost(post) {
  //setTimeout is non-blocking
  setTimeout(() => {
    posts.push(post);
  }, 4000);
}

You will basically not wait for the post to be pushed to the list. That is the reason it is not shown. if the interval in GetPosts was smaller than the on in CreatePost than it would display. It has nothing to do with async/await. The call to CreatePost is first of all not async because you are not returning a Promise. The Second thing is that setTimeout will not block, so every code that runs after can basically be done before the callback will be called.

To make CreatePost really async just look at the answer of @axiac Function is being called before await finishes Note: setTimeout is still non-blocking and the code won't work as expected. If this wasn't just exercise, I would say get rid of the setTimeouts.

Tom Siegel
  • 236
  • 1
  • 7