【问题标题】:How to chain promises in series in a while loop如何在while循环中串联promise
【发布时间】:2019-06-01 00:53:25
【问题描述】:

我必须连续查询多个数据库。基本上是一组必须按顺序完成的承诺。我在一个while循环中实现它。数据库根据日期命名。我还必须将请求延迟设置为 400 毫秒乘以每个承诺之间通过的循环数量(400*count)。如果有帮助,我将使用 cloudant 作为我的数据库,类似于 mongodb/couchdb,但它是一个在线实例。

所以我实现了一个查询函数,它将获取开始日期和结束日期,并检索范围内的所有值。如果日期相同,则它将查询一个数据库,否则它将在日期范围之间的每一天查询多个数据库。我主要在 if 语句中的 else 块遇到问题,如下所示。

db.predQuery = (start, end, locationCode) => {
        return new Promise((resolve, reject) => {
            let data = [];
            const startDate = moment(start, "MM/DD/YYYY");
            const endDate = moment(end, "MM/DD/YYYY");
            if (startDate.diff(endDate) === 0) {
                // THIS IF BLOCK WORKS GOOD
                let dbName = `prediction_${String(locationCode)}_`;
                dbName += startDate.format("YYYY-MM-DD");
                const db = this.cloudant.use(dbName);
                return db.find({selector: {_id: {'$gt': 0}}}).then((body) => {
                    data = body.docs;
                    return resolve(data);
                });
            } else {
                // THIS BLOCK IS WHERE THE PROBLEM IS

                // This is to start off the promise chain in the while loop
                let chainProm = Promise.resolve(1);
                let iterator = moment(startDate);
                let count = 0;

                // While loop for the series of promises
                while (iterator.isBefore(endDate) || iterator.isSame(endDate)) {
                    //dbName Format: prediction_0_2019-05-28
                    let dbName = `prediction_${String(locationCode)}_`;
                    dbName += iterator.format("YYYY-MM-DD");
                    const db = this.cloudant.use(dbName);
                    count += 1;

                    // Set off chain of promises
                    chainProm = chainProm.then(() => {
                        setTimeout(()=>{
                            db.find({selector: {_id: {'$gt': 0}}}).then((body) => {
                                // Keep adding on to the old array
                                data = data.concat(body.docs);
                            });
                        },count*400);
                    });

                    // Move to the next day
                    iterator.add(1,'days');
                }
                // Once all done resolve with the array of all the documents
                return resolve (data);
            }
        })
    };

基本上,输出应该是日期范围内所有文档的数组。现在单个日期有效,但是当我执行一系列日期时,请求没有通过,或者我达到了限制,或者它说承诺永远不会得到解决。我认为这可能不是解决此问题的最佳方法,并且对任何解决方案持开放态度。非常感谢任何帮助。

【问题讨论】:

  • chainProm = chainProm.then(() => { 这部分的代码根本没有返回一个 Promise - 事实上,没有返回语句

标签: javascript node.js promise cloudant


【解决方案1】:

您的chainProm 没有与predQuery 的外部调用连接 - 正在立即调用resolve

您可能会发现使用async/await 会容易得多,延迟内循环逻辑会更容易理解:

const delay = ms => new Promise(resolve => setTimeout(resolve, ms));
db.predQuery = async (start, end, locationCode) => {
  const startDate = moment(start, "MM/DD/YYYY");
  const endDate = moment(end, "MM/DD/YYYY");
  if (startDate.diff(endDate) === 0) {
    // THIS IF BLOCK WORKS GOOD
    let dbName = `prediction_${String(locationCode)}_`;
    dbName += startDate.format("YYYY-MM-DD");
    const db = this.cloudant.use(dbName);
    const body = await db.find({selector: {_id: {'$gt': 0}}});
    return body.docs;
  }
  const iterator = moment(startDate);
  const data = [];
  const testShouldIterate = () => iterator.isBefore(endDate) || iterator.isSame(endDate);
  let shouldIterate = testShouldIterate();
  while (shouldIterate) {
    //dbName Format: prediction_0_2019-05-28
    let dbName = `prediction_${String(locationCode)}_`;
    dbName += iterator.format("YYYY-MM-DD");
    const db = this.cloudant.use(dbName);
    const body = await db.find({selector: {_id: {'$gt': 0}}});
    data.push(body.docs);
    // Move to the next day
    iterator.add(1,'days');
    shouldIterate = testShouldIterate();
    // Don't delay on the final iteration:
    if (!shouldIterate) {
      break;
    }
    await delay(400);
  }
  // Once all done resolve with the array of all the documents
  return data;
};

通过这个实现,任何错误都会发送给predQuery的调用者(错误会导致它返回的Promise被拒绝),所以在调用predQuery时,一定要在后面加上catch

【讨论】:

    【解决方案2】:

    我还没有完全理解你的代码。但是,如果查询彼此独立,Promise.all() 似乎可以解决您的问题:

    https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/all

    如果您实际上需要以同步顺序运行 Promise,请尝试在循环中等待 Promise 解析。

    【讨论】:

      猜你喜欢
      • 2019-02-13
      • 2016-05-18
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2022-09-23
      相关资源
      最近更新 更多