Promise .then alert runs before the previous then

I got this weird behaviour a few days ago:

posts.addEventListener('click', event => {
  if (event.target.nodeName === 'BUTTON') {
    const postId = event.target.closest('li').id;
    sendRequest(
      'DELETE',
      `https://jsonplaceholder.typicode.com/posts/${postId}`
    )
      .then(() => document.getElementById(postId).remove()) // appears to run after the next .then
      .then(() => { alert('This post has been deleted successfully') // appears to run before the remove() method
        // setTimeout(
        //   () => alert('This post has been deleted successfully'),
        //   1000
        // );
      })
      .catch(e => alert(e.message));
  }
});

I don’t understand why the last .then block in the chain runs before the previous .then with remove().

In the DOM tree it appears that the remove() method runs almost at the same time with the alert function but the element is removed as the alert box is dropping. Visually, it’s removed from the page after the alert though.

With the setTimeout it works fine.

async function sendRequest(method, url, data) {
  return fetch(url, {
    method,
    body: JSON.stringify(data),
    headers: {
      'Content-type': 'application/x-form',
      type: 'json'
    }
  }).then(status).then(json).catch(console.log);
}

function json(data) {
  console.log(data);
  return data.json();
}

async function fetchPosts() {
  try {
    const responseData = sendRequest(
      'GET',
      'https://jsonplaceholder.typicode.com/posts'
    );

    for (const post of await responseData) {
      const postEl = document.importNode(postTemplate.content, true);
      postEl.querySelector('h2').textContent = post.title.toUpperCase();
      postEl.querySelector('p').textContent = post.body;
      postEl.querySelector('li').id = post.id;
      listElement.append(postEl);
    }
  } catch (e) {
    alert(e.message);
  }
}

function status(response) {
  if (response.status >= 200 && response.status < 300) {
    // console.dir(response)
    return response;
  } else {
    console.log(response);
    return Promise.reject(new Error('something bad happend'));
  }
}

For the linked part, if that does not provide much information:

async function createPost(title, content) {
  const userId = Math.random().toFixed(12).slice(2);
  const post = {
    title,
    body: content,
    userId
  };

  await sendRequest(
    'POST',
    'https://jsonplaceholder.typicode.com/posts',
    post
  );
  alert('This post has been added successfully');
}

fetchBtn.addEventListener('click', () => {
  event.stopPropagation();
  fetchPosts();
});

form.addEventListener('submit', event => {
  event.preventDefault();
  createPost(
    event.currentTarget.querySelector('input').value,
    event.currentTarget.querySelector('textarea').value
  );
});

posts.addEventListener('click', event => {
  if (event.target.nodeName === 'BUTTON') {
    const postId = event.target.closest('li').id;
    sendRequest(
      'DELETE',
      `https://jsonplaceholder.typicode.com/posts/${postId}`
    )
      .then(() => document.getElementById(postId).remove())
      .then(() => { alert('This post has been deleted successfully') // runs before the remove() method
        // setTimeout(
        //   () => alert('This post has been deleted successfully'),
        //   1000
        // );
      })
      .catch(e => alert(e.message));
  }
});

document.getElementById(postId).remove() doesn’t return promise, so the next .then() is executed without waiting.

You could try to replace it with:

.then(() => Promise.resolve(document.getElementById(postId).remove()))

(didn’t test it, writing from memory)

It’s contrary to what i learnt!

I learnt everything in the previous .then block will finish running before the next .then block have a chance to run.

If i assume this is true, i still think the alert function will have to queue for the stack to be empty or something - i don’t really know

still the same.

They say alert blocks page load. But the alert should really run after remove() has ran - i think

What happens if you put this inside alert?:

alert(JSON.stringify(document.getElementById(postId)))

If it’s null, then the element is removed.

1 Like

Really Null! But it was a bit misleading. Thank you for that code! That explains it.