【问题标题】:Complex promises and for loops复杂的 Promise 和 for 循环
【发布时间】:2019-01-23 21:44:31
【问题描述】:

一段时间以来,我一直在为复杂的 for/promise 化妆而苦苦挣扎。我不能使用 async/await,因为 azure functions v1 不支持它。

问题如下...

我得到了一个数组allArray

现在我想完成类似下面的事情,但我似乎很难过。

编辑: 问题似乎是函数到达loop并通过for循环发送allArray[0],我得到context.log("One: " + results[0], "Two: " + results[1]);,但它在发送空anotherArray之前没有等待,它也没有启动var call下一个循环也不行。

我的日志如下所示:

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

任何帮助将不胜感激。

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
    });
}

【问题讨论】:

  • 这段代码有什么地方不工作吗?
  • 道歉 - 见编辑
  • 我在oneAnalysis 上没有看到任何解决或拒绝的呼叫。它是丢失还是我应该假设它被某些外部依赖项调用?
  • @ErickRuizdeChavez 在之前的承诺/循环中我不必总是解决/拒绝 - 我 return 而是在下一层解决/拒绝,如果它到达那里(即在创建消息时)跨度>
  • 我正在重构您的“伪”代码。我注意到的一件事是你有额外的承诺。我稍后会将其作为答案发布,但我无法对其进行测试,因为它不会按原样工作。

标签: javascript node.js for-loop promise resolve


【解决方案1】:

您的loop 方法对我来说似乎有问题,您没有等待循环中生成的承诺解决后再对anotherArray 采取行动,这可能是您看到奇怪行为的原因。

在尝试解析 anotherArray 之前,您应该重新构造该方法,以便它正确解析旨在影响 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);
    });
}

【讨论】:

  • 我之前没用过map,你能具体说明一下它是如何工作的吗? elem 是数组中的每个对象吗?是否有可能知道它是哪个索引? (数组中的第一个或第二个对象)
  • Array.map() 通过将原始值“映射”到新数组来创建新数组。在这里,我将原始元素映射到 Promise,当解析时将调用 callCF() 并将返回的承诺添加到 anotherArray。索引作为第二个参数传递给回调,所以如果你向回调添加另一个参数,你可以使用索引。
  • 好的,我明白了,您编辑了数组中的每个对象。每个对象的变量名是elem。非常感谢,这让一切看起来如此简单!
  • mapreduce 非常强大,尽管在这种情况下我更喜欢(正如你在我的回复中看到的那样)reduce。另外,我强烈建议将它们作为原始代码中的单独函数保留,而不是在单个函数中执行所有操作;它会更容易阅读,也更容易测试(一个函数只能做一件事)。
【解决方案2】:

async/await 很好,但最终它们只是常规承诺之上的糖。正如我在 cmets 中对您的问题所提到的,我正在对您的伪代码做出一些假设,并按照我的理解重构代码。

既然你可以使用 Promise,我假设你也可以使用 const 和箭头函数;如果不是这样,请告诉我,我可以重构我的代码。

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
  });
}

【讨论】:

  • 我之前没有使用 reduce 作为函数类型(仅用于减少对象数组以删除一些属性!)。因此,在这种情况下,我需要一点时间才能理解这一切!谢谢您的帮助! item是你设置的变量名吗?
  • reduce 非常强大,当您不仅需要map 一个数组,还需要转换返回值的整个结构时。 Reduce 的回调与常规映射略有不同,因为它具有用于累积结果的额外参数以及可选的初始状态。我强烈建议您熟悉它,因为它在解析和转换输入时非常有用。 developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…
猜你喜欢
  • 2020-09-23
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多