Search code examples
javascriptasynchronouspromisejquery-deferreddeferred

waiting for JS async func to complete before running some sync code


I have an async function getDataItem that returns a promise and passes data to another function preparePhysicianSchemaData which builds a global object physicianDetailsObj out of the passed data as well as data fetched after calling yet another async function inside it for each row of data it was initially passed.

getDataItem(listID, itemID).then(preparePhysicianSchemaData)

Only after the global object variable physicianDetailsObj is fully populated, then I need to call another function called buildSEOSchemaBlock(), whose job is to parse physicianDetailsObj object and build the final object needed.

I would rather not use setTimeOut to try to time this:

      setTimeout(function(){ return getListItem(listID, itemID).then(preparePhysicianSchemaData) }, 10);
      setTimeout(function(){ return buildPhysicianSchemaBlock() }, 3000); 

How can I chain the last function like this: getDataItem(listID, itemID).then(preparePhysicianSchemaData).then(buildPhysicianSchemaBlock) ensuring that the last function runs only after the global object variable physicianDetailsObj is fully populated?

    var physicianDetailsObj = {};
    function getListItem() {}  //returns promise
    function preparePhysicianSchemaData(item) {
        var tempPhysicianDetailsObj = {};   
        var currentPhysicianItemId = item.get_id();
    
        tempPhysicianDetailsObj = {
            "name" : item.get_item("Title"),
            "url" : item.get_item("SEOCanonicalHref").match('href="([^"]+)')[1]
        };
     
         var currentItemPhysicianTag= item.get_item("PhysicianItemTag").get_label();  
    
         getPhysicianLocationDetailsFromServiceLocations(currentItemPhysicianTag).then(function(slitems) {
            console.log(slitems);
            var slitemEnum = slitems.getEnumerator();
    
            //first empty the object
            Object.keys(physicianDetailsObj).forEach(k => delete physicianDetailsObj[k]);
    
            while (slitemEnum.moveNext()) {
                var slitem = slitemEnum.get_current();
                physicianDetailsObj[currentPhysicianItemId + '-' + slitem.get_id()] = {
                    "name":  tempPhysicianDetailsObj["name"],
                    "image": tempPhysicianDetailsObj["image"],
                    "url": tempPhysicianDetailsObj["url"],
                    "streetAddress": slitem.get_item("LocationAddress"),
                    "addressLocality": slitem.get_item("LocationLU_x003A_LocationCity").get_lookupValue()
                }
            }
         }); 
    
    }

function buildSEOSchemaBlock(){ }  //process physicianDetailsObj

getPhysicianLocationDetailsFromServiceLocations is an async function which is called inside preparePhysicianSchemaData


Solution

  • If preparePhysicianSchemaData is synchronous then you don't need to await it, just perform the operation after it. Something like this:

    getListItem(listID, itemID).then(function() {
      preparePhysicianSchemaData();
      buildPhysicianSchemaBlock();
    });
    

    Or if there are results you need from the Promise, something like:

    getListItem(listID, itemID).then(function(result) {
      preparePhysicianSchemaData(result);
      buildPhysicianSchemaBlock();
    });
    

    If it's asynchronous then you can chain the Promises, something like:

    getListItem(listID, itemID)
      .then(function(result) { return preparePhysicianSchemaData(result); })
      .then(function(newResult) { return buildPhysicianSchemaBlock(newResult); });
    

    Basically each call to .then() passes the result of the previous Promise to the new asynchronous function, returning that function's Promise.