在循环再次迭代之前,如何等待promise的"then"块在嵌套的forEach循环中执行?

Mju*_*ice 0 javascript foreach node.js

我有3个forEach循环,我希望前两个等待一个promise然后阻止执行,然后再重新开始迭代.我已经使用标记为1,2和3的console.log语句证明了这一点.

我希望控制台日志按顺序排列,如"1,2,3,1,2,3等"而不是我得到的是"1,2,1,2,1,2,1,2,1" ,2,3,3,3,3,3,3"如何使所有这些顺序?fetchCandidatesByCity是mongoose find方法,它返回一个promise.

axios.get(FEED_URL).then(data => {
let candidatesWithMessageSent = [];

data.data.jobs.forEach(job => {
  console.log("1");
  // console.log(candidatesWithMessageSent);
  job.city.forEach(cityAndState => {
    console.log("2");

    let jobState = extractState(cityAndState);

    let jobLocation = addSpaceAfterComma(cityAndState.toLowerCase());

    let jobCity = extractCity(jobLocation);


    fetchCandidatesByCity(jobCity)
      .then(candidates => {
        candidates.forEach((candidate, index) => {
          console.log("3");
          const candidateId = candidate._id.toString();

          if (index <= MAX_MESSAGE_LIMIT &&
            !containsUberLyft(job.title) &&
            priceIsHigh(job.price) &&
            !candidateHasMessage(candidatesWithMessageSent, candidateId)) {

            const jobURL = `http://www.jobs2careers.com/click.php?id=${job.id}.${PUBLISHER_ID}`;

            // candidate has received a job notification, so add candidate to candidatesWithMessageSent

            candidatesWithMessageSent = [ ...candidatesWithMessageSent, candidateId ];

          }
          return;
        });
      })
      .catch((error) => {
        console.log(error);
      });
  });
});
Run Code Online (Sandbox Code Playgroud)

});

jfr*_*d00 7

使用异步操作串行迭代数组的常见设计模式是使用.reduce()累积promise的位置.概括地说,它的工作原理如下:

array.reduce(function(p, item) {
    return p.then(function() {
        return someAsyncPromise(item);
    });
}, Promise.resolve()).then(function(finalValue) {
    // all done here
}).catch(function(err) {
    // error here
});
Run Code Online (Sandbox Code Playgroud)

Promise.resolve()作为累加器的初始值传入.循环的每次迭代,然后return p.then(someOperation).

这基本上将一大堆操作链接在一起,如下所示:

Promise.resolve().then(...).then(...).then(...).then(...).then(...)
Run Code Online (Sandbox Code Playgroud)

每次.then()处理程序调用自定义函数时,它都会传递给数组的下一个迭代值,并返回一个将在下一次f调用之前等待的promise .


由于您有两个嵌套的数组迭代循环,您可以将一个嵌套在另一个内部,如下所示:

myData.reduce(function(p, nestedArray) {
    return p.then(function() {
        return nestedArray.reduce(function(p2, item) {
            return p2.then(function() {
                return someAsyncPromise(item);
            });
        }, Promise.resolve());
    });
}, Promise.resolve()).then(function(finalVal) {
    // all done here
}).catch(function(err) {
    // error here
});
Run Code Online (Sandbox Code Playgroud)

或者,如果您没有使用来自外部的先前迭代的promise promise结果reduce(),您可以简化这样的一点:

myData.reduce(function(p, nestedArray) {
    return nestedArray.reduce(function(p2, item) {
        return p2.then(function() {
            return someAsyncPromise(item);
        });
    }, p);
}, Promise.resolve()).then(function(finalVal) {
    // all done here
}).catch(function(err) {
    // error here
});
Run Code Online (Sandbox Code Playgroud)

这是Bluebird promise库使事情变得更容易的地方,因为您可以使用Promise.mapSeries()串行迭代数组并累积结果数组:

 Promise.mapSeries(myData, function(nestedArray) {
     return Promise.mapSeries(nestedArray, function(item) {
         return someAsyncPromise(item);
     });
 }).then(function(results) {
    // all done here
 }).catch(function(err) {
     // error here
 });
Run Code Online (Sandbox Code Playgroud)

在所有这些场景中,您可以将最终结果累积到一个对象中,该对象成为最终承诺的已解析值,或者您可以将其用于副作用,其中您在更高范围内定义了一些其他对象,然后您完成后使用该对象.