Kleber Mota
Kleber Mota

Reputation: 9065

How Promises are executed inside a Javascript function?

If I have a function with 3 Promises, they are supposed to run one after another, in the other they are appeared in the code of the function? I have a code where, in the third and last promise, I should have access to variables size and total, assigned inside the other 2 promises, but they remain undefined. Am I missing something about this concept of Promisses? I just start learning about them very recently.

my code:

function update_cart() {
  var cart_size = document.getElementById('cart_size');
  var cart_status = document.getElementById('cart_status');
  var cart_total = document.getElementById('cart_total');

  var size, total;

  var promise1 = new Promise(function(resolve, reject) {
    if(cart_size !== null) {
      var cliente = cart_size.dataset.cliente;
      var url = cart_size.dataset.url;

      var xhr = new XMLHttpRequest();
      xhr.open("POST", url, true);
      xhr.onreadystatechange = function() {
        if (xhr.readyState == 4 && xhr.status == 200) {
          resolve(xhr.responseText);
        }
      };
      var formData = new FormData();
      formData.append("cliente", cliente);
      xhr.send(formData);
    } else {
      reject();
    }
  });

  promise1.then(function(value) {
    size = value;
    cart_size.innerHTML = size;
    if(size == 0)
      cart_size.style.display = 'none';
    else
      cart_size.style.display = 'block';
  }).catch(function(){
    console.log('erro1');
  });

  var promise2 = new Promise(function(resolve, reject) {
    if(cart_total !== null) {
      var cliente = cart_total.dataset.cliente;
      var url = cart_total.dataset.url;

      var xhr = new XMLHttpRequest();
      xhr.open("POST", url, true);
      xhr.onreadystatechange = function() {
        if (xhr.readyState == 4 && xhr.status == 200) {
          resolve(xhr.responseText);
        }
      };
      var formData = new FormData();
      formData.append("cliente", cliente);
      xhr.send(formData);
    } else {
      reject();
    }
  });

  promise2.then(function(value) {
    total = value;
    var currency = new Intl.NumberFormat('pt-BR', { style: 'currency', currency: 'BRL' }).format(total);
    cart_total.innerHTML = currency;
  }).catch(function(){
    console.log('erro2');
  });

  var promise3 = new Promise(function(resolve, reject) {
    if(cart_status !== null) {
      resolve();
    } else {
      reject();
    }
  });

  promise3.then(function() {
    console.log('promise3');
    console.log('size: '+size+', total: '+total);
    switch(size) {
      case 0:
        cart_status.innerHTML = 'A cesta de compras está vazia.';
        document.getElementById('table').style.display = 'none';
        break;
      case 1:
        cart_status.innerHTML = size + ' produto adicionado na cesta, com valor ' + total;
        document.getElementById('table').style.display = 'block';
        break;
      default:
        cart_status.innerHTML = size + ' produtos adicionados na cesta, com valor ' + total;
        document.getElementById('table').style.display = 'block';
        break;
    }
  }).catch(function(){
    console.log('erro3');
  });
}

Upvotes: 2

Views: 59

Answers (2)

RomanK
RomanK

Reputation: 154

The three promises are being executed at the same time since you are initializing them before waiting for any to finish. If you want the promises to run one after the other and be able to catch different errors for different promises you should use async/await:

async function update_cart() {
  // ...
  try {
    var value = await promise1;
    size = value;
    // Rest of code inside promise1.then
  } catch(err) {
    console.log('erro1');
  }
}

Repeat that for promise2 and promise3. The function update_cart will now return a Promise that resolves when promise1, promise2 and promise3 all resolve, one after the other

Upvotes: 1

Easwar
Easwar

Reputation: 5402

Promise execution does not block the calling function. Hence your all 3 promises are executed immediately resulting in the undefined value in size and total variables. Try this :

function update_cart() {
  var cart_size = document.getElementById("cart_size");
  var cart_status = document.getElementById("cart_status");
  var cart_total = document.getElementById("cart_total");

  var size, total;

  var promise1 = new Promise(function(resolve, reject) {
    if (cart_size !== null) {
      var cliente = cart_size.dataset.cliente;
      var url = cart_size.dataset.url;

      var xhr = new XMLHttpRequest();
      xhr.open("POST", url, true);
      xhr.onreadystatechange = function() {
        if (xhr.readyState == 4 && xhr.status == 200) {
          resolve(xhr.responseText);
        }
      };
      var formData = new FormData();
      formData.append("cliente", cliente);
      xhr.send(formData);
    } else {
      reject();
    }
  });

  var promise2 = new Promise(function(resolve, reject) {
    if (cart_total !== null) {
      var cliente = cart_total.dataset.cliente;
      var url = cart_total.dataset.url;

      var xhr = new XMLHttpRequest();
      xhr.open("POST", url, true);
      xhr.onreadystatechange = function() {
        if (xhr.readyState == 4 && xhr.status == 200) {
          resolve(xhr.responseText);
        }
      };
      var formData = new FormData();
      formData.append("cliente", cliente);
      xhr.send(formData);
    } else {
      reject();
    }
  });

  var promise3 = new Promise(function(resolve, reject) {
    if (cart_status !== null) {
      resolve();
    } else {
      reject();
    }
  });

  promise1
    .then(function(value) {
      size = value;
      cart_size.innerHTML = size;
      if (size == 0) cart_size.style.display = "none";
      else cart_size.style.display = "block";

      promise2
        .then(function(value) {
          total = value;
          var currency = new Intl.NumberFormat("pt-BR", {
            style: "currency",
            currency: "BRL"
          }).format(total);
          cart_total.innerHTML = currency;

          promise3
            .then(function() {
              console.log("promise3");
              console.log("size: " + size + ", total: " + total);
              switch (size) {
                case 0:
                  cart_status.innerHTML = "A cesta de compras está vazia.";
                  document.getElementById("table").style.display = "none";
                  break;
                case 1:
                  cart_status.innerHTML =
                    size + " produto adicionado na cesta, com valor " + total;
                  document.getElementById("table").style.display = "block";
                  break;
                default:
                  cart_status.innerHTML =
                    size + " produtos adicionados na cesta, com valor " + total;
                  document.getElementById("table").style.display = "block";
                  break;
              }
            })
            .catch(function() {
              console.log("erro3");
            });
        })
        .catch(function() {
          console.log("erro2");
        });
    })
    .catch(function() {
      console.log("erro1");
    });
}

I just moved the next promise's invocations the the previous promise's callback, so that each one gets executed only after the previous one is complete. If you require some other order, or need to parallelize requests read more about promises here.

Upvotes: 1

Related Questions