简体   繁体   中英

Complex promises and for loops

I have been struggling with a complex for/promise makeup for a while. I cannot use async/await as azure functions v1 does not support it.

The problem is as follows...

I have been give an array allArray

Now I want to complete something like below, but I seem to been having a hard time.

EDIT: The issue seems to be that the function reaches loop and sends allArray[0] through the for loop and I get context.log("One: " + results[0], "Two: " + results[1]); back, but it does not wait before sending an empty anotherArray , it also does not start var call nor do the next loop.

My logs look like:

no thanks (oneAnalysis)
No things (twoAnalysis)
Done again
[]
Done
One: " + results[0], "Two: " + results[1]
Function completed

Any help would be greatly appreciated.

module.exports = function (context, allArray) {
    var loopPromise = loop(context, allArray);
        Promise.all([loopPromise]).then(function(results){ 
            context.log('Done again');
            context.log(results[0]);
            context.done() 
        });
}

function loop (context, allArray) {
    var anotherArray = [];
    for (i = 0; i < allArray.length; i++) {
        var promiseOne = oneAnalysis (context, allArray[i]);
        var promiseTwo = twoAnalysis (context, allArray[i]); 

        Promise.all([promiseOne, promiseTwo]).then(function(results){ 
            context.log('Done');
            context.log("One: " + results[0], "Two: " + results[1]);

            var Id = allArray[i].Id;
            var call = callCF (context, Id)
            anotherArray.push(call);
        });
    }
    return Promise.all(anotherArray);
}

function oneAnalysis (context, input) {

    if (input.something.length > 0) {
        var somethingArray = [];
        for (i = 0; i < input.something.length; i++) {
            if (stuff) {
                var queue = queue;
                var text = {
               text                
}
                var create = createMessage(text, queue, context);
                    somethingArray.push(create);
            } else {
                somethingArray.push("no thanks");
            }
        }
        return Promise.all(somethingArray);
    } else {
        return ("No things");
    }
}

function twoAnalysis (context, input) {
same as oneAnalysis
}

function createMessage(text, queue, context) {
    return new Promise((resolve, reject) => {

        queueService.createMessage(queue, JSON.stringify(text), function(error) {
            if (!error) {
                context.log('Message inserted:', text);
                resolve ('Message inserted: ' + text)
            }
            else {
                context.log('All done'); 
                resolve ('error');
            }
        });
    });
}

function callCF(context, id) {
    return new Promise((resolve, reject) => {
        Do http request and resolve on end
    });
}

Your loop method looks problematic to me, you're not waiting for the promises generated in the loop to resolve before acting on anotherArray which is likely why you are seeing strange behavior.

You should restructure the method so that it properly resolves the promises that are intended to affect anotherArray before attempting to resolve anotherArray .

function loop (context, allArray) {
    var anotherArray = [];

    var processing = allArray.map(function(elem) {
        var promiseOne = oneAnalysis(context, elem);
        var promiseTwo = twoAnalysis(context, elem);
        return Promise.all([promiseOne, promiseTwo]).then(function(results){
            context.log('Done');
            context.log("One: " + results[0], "Two: " + results[1]);

            var Id = elem.Id;
            var call = callCF(context, Id)
            anotherArray.push(call);
        });
    });

    return Promise.all(processing).then(function() {
        return Promise.all(anotherArray);
    });
}

async / await are nice, but in the end they are just sugar on top of regular promises. As I mentioned in my comments to your question, I am making some assumptions of your pesudo code and refactoring code as I understand it.

Since you are able to use promises, I assume you can also use const and arrow functions; if that is not the case let me know and I can refactor my code.

module.exports = (context, allArray) => {
  // loop returns a promise already from Promise.all so we do not need to wrap
  // it in another promise.
  loop(context, allArray).then(results => {
    context.log('Done again');
    context.log(results[0]);
    context.done()
  });
}

function loop(context, allArray) {
  // this reduce will take every item in `allArray`
  const anotherArray = allArray.reduce((accumulator, item) => {
    // For each item in anotherArray, call oneAnalysis and twoAnalysis; return 
    // values are promises and we need to wait for them to resolve using 
    // Promise.all.
    const promiseOne = oneAnalysis(context, item);
    const promiseTwo = twoAnalysis(context, item);
    const callCFPromise = Promise.all([promiseOne, promiseTwo]).then(results => {
      context.log('Done');
      context.log('One: ' + results[0], 'Two: ' + results[1]);

      const Id = allArray[i].Id;

      // Once promiseOne and promiseTwo are resolved call callCF which returns
      // another promise itself. This is the final promised to be returned to
      // callCFPromise.
      return callCF(context, Id);
    });

    // callCFPromise will be a promise to be resolved with the value of callCF 
    // after promiseOne and promiseTwo are resolved. We are inside a reduce so
    // we need to push our promise in the accumulator and return it for the next
    // iteration.
    accumulator.push(callCFPromise);

    return accumulator;
  }, []);

  return Promise.all(anotherArray);
}

function oneAnalysis(context, input) {
  if (!input.something.length) {
    return 'No things';
  }

  // Take every item in `input.something`, and if `stuff` is truthy, it will 
  // call createMessage, and push the promise to accumulator. in the end, the 
  // resulting accumulator (array of promises) will be returned. In the original
  // version of this code I did not see any reference input.something[i] so in
  // this case I am not doing anything to item, but I guess it will be used for
  // something.
  const createPromises = input.something.reduce((accumulator, item) => {
    if (!stuff) {
      photoArray.push('no thanks');
    } else {
      // I am guessing these queue and text have something to do with item (which
      // is input.something[i] in the original shared code)
      const queue = queue;
      const text = { text };
      const create = createMessage(text, queue, context);

      // Since we are in a reduce, we need to push our create promise into the
      // accumulator and return it for the next iteration. In this case we only
      // push the promises we create instead of every iteration.
      accumulator.push(create);
    }

    return accumulator;
  }, []);

  // Take the resulting accumulator of promises and create a new promise that
  // resolves when every promise in the array is resolved.
  return Promise.all(createPromises);
}

function twoAnalysis(context, input) {
  // same as oneAnalysis
}

function createMessage(text, queue, context) {
  return new Promise((resolve, reject) => {
    queueService.createMessage(queue, JSON.stringify(text), error => {
      if (error) {
        return reject(error);
      }

      context.log('Message inserted:', text);
      return resolve('Message inserted: ' + text);
    });
  });
}

function callCF(context, id) {
  return new Promise((resolve, reject) => {
    // Do http request and resolve on end
  });
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM