Search code examples
javascriptasync-awaites6-promise

Follow on actions after asynch function completion


I'm going to do my best to explain this one since this is a reduced version of the real code and some parts might seem redundant or excessive (and they might be). The model can be found at https://jsfiddle.net/ux5t7ykm/1/. I am using a function to build an array of values that are passed to another function to do a REST call for the data that corresponds to the values. The array lists the name of the list to get the data from, the field name, the filter field name, the filter value, and the target array to pass the autocomplete values back to. This part is working fine and is essentially what Step1() does. setAutocomplete() then uses getData() to make the multiple REST calls. I'm using sprLib for that part so it's already a promise (this is getListItems() normally). getData() is where the magic is happening. It recursively cycles through the list and performs getListItems() until all the data is retrieved. I have this set as an async function with await. This also seems to be running fine.

The problem I have is that once this whole sequence is complete, I then have another function that I need to run. It manipulates the data from the previous function. I've tried to put step1() in a promise, make it async, etc. Nothing seems to work. CheatStep() seems to be where a response or some other method signaling to Step1() should go to then allow the next function to run. I've been banging my head against this all week and I feel like my understanding of promises and asnyc is regressing.

var dict = [{
  'id': '1',
  'title': 'test1'
}, {
  'id': '2',
  'title': 'test2'
}, {
  'id': '3',
  'title': 'test3'
}, {
  'id': '4',
  'title': 'test4'
}, ]

step1()
nextStep()

function nextStep() {
  console.log('I run after Step1')
}

function cheatStep() {
  console.log('I cheated')
}

function step1() {
setAutoComplete(4);
}

function setAutoComplete(listIndex) {
  getData(listIndex,
    function(result) {
      for (var i = 0; i < listIndex; i++) {
        var autoDict = result[i];
        console.log(autoDict)
      }
      cheatStep()
    },
    function(e) {
      console.error(e);
      alert('An error occurred wile setting autocomplete data for item');
    })
    
}

async function getData(listIndex, success, error, curIndex, result) {
  var curIndex = curIndex || 0;
  var result = result || {};
  await getListItems(curIndex,
    function(listItems) {
      result[curIndex] = listItems;
      curIndex++;

      if (listIndex > curIndex) {
        getData(listIndex, success, error, curIndex, result);
        console.log('1');
      } else {
        console.log('2');
        success(result);
      }
    },
    error)
}

function getListItems(curIndex, success, error) {
  new Promise(
      function(resolve, reject) {
        var x = dict[curIndex];
        resolve(x);
      })
    .then(function(x) {
      success(x);
    });
}

Solution

  • I have adopted your code to a more async/await view coz you still use heavily callback approach:

    var dict = [{
      'id': '1',
      'title': 'test1'
    }, {
      'id': '2',
      'title': 'test2'
    }, {
      'id': '3',
      'title': 'test3'
    }, {
      'id': '4',
      'title': 'test4'
    }, ]
    
    function nextStep(result) {
      console.log('I run after Step1',result)
    }
    
    function cheatStep() {
      console.log('I cheated')
    }
    
    step1().then((result)=>{nextStep(result)})
    
    
    async function step1() {
    return await setAutoComplete(4);
    }
    
    async function setAutoComplete(listIndex) {
      const result = await getData(listIndex);
      for (var i = 0; i < listIndex; i++) {
            var autoDict = result[i];
            console.log(autoDict)
          }
          cheatStep()
          return result
        
    }
    
    async function getData(listIndex, curIndex=0, result={}) {
      const listItems =  await getListItems(curIndex)
      result[curIndex] = listItems;
      curIndex++;
        if (listIndex > curIndex) {
            return await getData(listIndex, curIndex, result);
          } else {
            console.log('2');
            return (result);
          }
    
    }
    
    function getListItems(curIndex) {
      return new Promise(
          (resolve, reject) =>{
            resolve(dict[curIndex]);
          })
    }
    
    

    I also kept recursion here, but personally I would prefer solution with a loop instead.

    Also a minor advise. Try to avoid write declaration after usage:

    nextStep()
    
    function nextStep() {
      console.log('I run after Step1')
    }
    

    JS hoisting does that a valid syntax but it is really hard to read=)