Как связать обещания в серии в цикле - PullRequest
2 голосов
/ 01 июня 2019

Я должен сделать запрос к нескольким последовательным базам данных. В основном набор обещаний, которые должны быть выполнены последовательно. Я реализую это в цикле пока. Базы данных названы на основе дат. Я также должен установить задержку запроса в 400 мс, умноженную на количество циклов (400 * число) между каждым обещанием. Если это поможет, я использую cloudant в качестве базы данных, аналогично mongodb / couchdb, но онлайн-экземпляру.

Итак, я реализовал функцию запроса, которая будет принимать начальную и конечную дату и извлекать все значения в диапазоне. Если даты совпадают, то он будет запрашивать одну базу данных, в противном случае он будет запрашивать несколько баз данных для каждого дня в диапазоне дат. В основном у меня проблемы с блоком else в операторе if, как вы можете видеть ниже.

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

Обычно предполагается, что выводом является массив всех документов в диапазоне дат. Теперь единственная дата работает, но когда я делаю диапазон дат, либо запрос не проходит, либо я достигаю предела, либо он говорит, что обещание никогда не будет выполнено. Я не думаю, что это может быть не лучшим способом приблизиться к этому, и я открыт для любого решения. Любая помощь с благодарностью.

Ответы [ 2 ]

4 голосов
/ 01 июня 2019

Ваш 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 (ошибка приведет к отклонению обещания, которое он возвращает), поэтому при вызове predQuery обязательно добавьте catch впоследствии.

0 голосов
/ 01 июня 2019

Я не полностью понял ваш код. Однако, если запросы не зависят друг от друга, похоже, что Promise.all() решит вашу проблему:

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

Если вы действительно хотите, чтобы обещания выполнялись в синхронном порядке, попробуйте просто дождаться разрешения обещаний в цикле.

...