0

I have the following method which returns values asynchronously. return inventoryData seems to fire before the rest of the values are populated. Here is the log:

enter image description here

I want to wait until all the values are retrieved before return inventoryData is fired. How would I do that?

function checkInventoryUrl(url, size, code) {
  var inventoryData = {};

  return $http.get(url).then(function(response) {
    var html = response.data;

    // returns value instantly
    inventoryData.url = url;

    // parses html, takes a few seconds to return value
    inventoryData.name = getProductName(html);
    inventoryData.price = getPrice(html);

    // returns promise
    getProductQty(html, size).then(function(result) {
      if(result) {
        inventoryData.qtyAvailable = result;
        console.log(inventoryData);
      }
    });

    // returns promise
    getProductMaxOrder(html, size).then(function(result) {
      if(result) {
        inventoryData.maxOrder = result;
        console.log(inventoryData);
      }
    });

    // returns promise
    getProductSize(html, size).then(function(result) {
      if(result) {
        inventoryData.size = result;
        console.log(inventoryData);
      }
    });

    // fired straight away returns empty object
    return inventoryData;
  });
}
methuselah
  • 12,766
  • 47
  • 165
  • 315

3 Answers3

2

JavaScript is single-threaded. A function can not be made to "wait". A function executes until it returns. It can only return values that are available or a pending promise that is fulfilled later when the data returns from the server.

To create a promise for inventoryData, use $q.all:

function checkInventoryUrl(url, size, code) {
  var inventoryData = {};

  return $http.get(url).then(function(response) {
    var html = response.data;

    // returns value instantly
    inventoryData.url = url;

    // parses html, takes a few seconds to return value
    inventoryData.name = getProductName(html);
    inventoryData.price = getPrice(html);

    // returns promise
    inventory.qtyAvailable = getProductQty(html, size);

    // returns promise
    inventoryData.maxOrder = getProductMaxOrder(html, size);

    // returns promise
    inventoryData.size = getProductSize(html, size);

    //RETURN composite promise
    return $q.all(inventoryData);
  });
}

The $q.all method accepts an object on which some or all of the properties of that object may be promises. It returns a promise which resolves fulfilled with all the fulfilled values of the individual promises or rejects with the value of the first rejected promise.

To use:

 checkInventory(url, size, code).then(function (inventoryData) {
     console.log(inventoryData);
 }).catch(function(errorResponse) {
     throw errorResponse;
 }); 
georgeawg
  • 48,608
  • 13
  • 72
  • 95
1

This is by design, and how promises work. You need to chain your promises to each other (in thens), and then you need to return inventoryData from the last one, or just return the promise and continue working from it.

Jim Deville
  • 10,632
  • 1
  • 37
  • 47
1

You could use Promise.all which can take an array of promises and when all the passed promises have resolved with be able to invoke a .then method call.

An example:

function checkInventoryUrl(url, size, code) { 
  var inventoryData = {}; 
  ....
  .... 
  inventoryData.url = url; // parses html, takes a few seconds to return value 
  inventoryData.name = getProductName(html); 
  var p0 = inventoryData.price = getPrice(html); // returns promise 
  var p1 = getProductQty(html, size); 
  var p2 = getProductMaxOrder(html, size);
  var p3 = getProductSize(html, size);

  Promise.all([p0, p1, p2, p3,])
    .then(function(values){
    // Called when all promises passed in resolve
    });
}
Pineda
  • 7,435
  • 3
  • 30
  • 45