Узел - Async для Loop не ждет, заставляет Mongo отключиться слишком рано - PullRequest
0 голосов
/ 19 апреля 2019

У меня есть массив объектов, которые я анализирую.Я проверяю Монго, чтобы увидеть, существует ли запись для записи.Если так, я обновляю запись.в противном случае я создаю запись.Это работает нормально.Однако после циклических процессов я хочу выполнить mongoose.disconnect ().Однако это происходит во время цикла.Я делал это раньше, но на этот раз немного по-другому, и мне не повезло.Единственное, что отличается, я вызываю другую функцию, которая требует ожидания при сохранении новых записей.

mongoose.set('useCreateIndex', true);
  mongoose
    .connect(db, { useNewUrlParser: true })
    .then(() => console.log('MongoDB Connected'))
    .then(async () => {
      await parseWIP(converted);
      mongoose.disconnect();
    })
    .catch(err => console.log(err));
});

function parseWIP(array) {
  return new Promise(async (resolve, reject) => {
    for (let wip of array) {

      WIP.findOne({ query_here })
        .then(async existingWip => {
          if (existingWip && existingWip.id) {
            // Update existing record

            const salesOrderState = getPatientReadableStatus(
              wip.work_in_progress_wip_state
            );

            existingWip.salesOrder.state = salesOrderState.state;
            existingWip.salesOrder.info = salesOrderState.info;
            existingWip.salesOrder.progress = salesOrderState.progress;
            existingWip.lastModified = moment.now();
            await existingWip.save().then(updatedWip => {
              console.log(`Updated WIP`);
            });
          } else {
            // Create new record
            await createNewWip(wip);
          }
        })
        .catch(err => {
          console.log(err);
          reject(err);
        });
    }
    resolve();
  });
}

function createNewWip(wip) {
  return new Promise(async (resolve, reject) => {
    let patientPhone = wip.patient_phone;
    if (!wip.patient_phone && wip.patient_mobile) {
      patientPhone = wip.patient_mobile;
    }

    const branch = await getBranchContactInfo(wip.sales_order_branch_office);
    const salesOrderState = getPatientReadableStatus(
      wip.work_in_progress_wip_state
    );

    let wipRecord = { ... objects ... };

    const entry = new WIP(wipRecord);

    await entry
      .save()
      .then(savedWipRecord => {
        console.log(savedWipRecord._id);
      })
      .catch(err => reject(err));
    resolve();
  });
}

Я пытался forEach для (пусть wip из массива) и для (пусть wip в массиве).Почему обещание возвращается немедленно?

Ответы [ 2 ]

2 голосов
/ 19 апреля 2019

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

Итак, решение будет примерно таким (непроверенным, но идея должна быть довольно простой)

function parseWIP(array) {
    return new Promise(async (resolve, reject) => {
        for (let wip of array) {
            //array.forEach(async wip => {
            //let wip = array[key];

            await WIP.findOne({ query_goes_here })
              .then(async existingWip => {
                if (existingWip && existingWip.id) {
                  // Update existing record
                  console.log(`Existing WIP: ${existingWip.id}`);
                  ... assign some values ...
                  existingWip.lastModified = moment.now();

                  await existingWip.save().then(updatedWip => {
                    console.log(`Updated WIP ${updatedWip.id}`);
                  });
                } else {
                  // Create new record
                  await createNewWip(wip);
                }
              })
              .catch(err => {
                  console.log(err);
                  reject(err);
                });
        }  
        console.log('here we are at end of loop'); 
        resolve();
    });
}
1 голос
/ 19 апреля 2019

Зачем смешивать async/await со стандартным синтаксисом Promise, когда вы можете просто написать все это одним стандартным способом. Кроме того, все методы mongoose возвращают Promise в любом случае, поэтому я не понимаю, почему вы даже пытаетесь обернуть обратный вызов Promise.

Листинг в основном показывает неправильное понимание Обещаний и async/await, поэтому приведенный здесь пример должен прояснить некоторые вещи:

// Require mongoose and add your model definitions

const uri = 'mongodb://localhost:27017/test';
const opts = { useNewUrlParser: true };

(async function() {

  try {

    const conn = await mongoose.connect(uri, opts);

    // get your 'data' from somewhere of course.

    for ( let wip of data ) {

      let existingWIP = await WIP.findOne(query_goes_here);

      if (existingWip) {             // Asking for "id" on null would be an error

        // Update existing record
        console.log(`Existing WIP: ${existingWip.id}`);
        ... assign some values ...
        existingWip.lastModified = moment.now();

        let updatedWip = await existingWip.save()
        console.log(`Updated WIP ${updatedWip.id}`); // though you should understand this does not change
                                                     // as id is immutable


      } else {
        let newWip = await WIP.create(wip);  // not sure why you are creating a function
                                           // but all mongoose methods return a promise anyway
        // maybe do something
       }

    }
  } catch (e) {
    console.error(e);
  } finally {
    mongoose.disconnect();
  }

})()

Отмечая, что await в основном означает, что вам не нужно делать then(), потому что на самом деле это одно и то же, но только с более чистым синтаксисом . То же самое относится к .catch(), так как вместо этого он становится намного чище try..catch.

Если нужно, разбейте это функционально, но если вы просто делаете быстрый скрипт для загрузки и обновления некоторых вещей, то, вероятно, в этом нет особого смысла. Просто убедитесь, что все реализации function() возвращают Promise (то есть результат метода mongoose) и что вы await их.

Кроме того, вы можете в основном взглянуть на findOneAndUpdate() и, в частности, на опцию "upsert". Одно это в основном удалит ваше условие if..then..else и сделает все это в одном запросе вместо разделенных find() и save()

// Require mongoose and add your model definitions

const uri = 'mongodb://localhost:27017/test';
const opts = { useNewUrlParser: true };

(async function() {

  try {

    const conn = await mongoose.connect(uri, opts);

    // get your 'data' from somewhere of course.

    for ( let wip of data ) {

      let updatedWip = await WIP.findOneAndUpdate(
        query_goes_here,
        update_statement_goes_here,
        { upsert: true, new: true }     // need these options
      );

      console.log(`Updated WIP ${updatedWip.id}`);
    }
  } catch (e) {
    console.error(e);
  } finally {
    mongoose.disconnect();
  }

})()

Или, конечно, если вам просто не нужно ничего делать в «цикле», тогда вы можете просто использовать bulkWrite():

// Require mongoose and add your model definitions

const uri = 'mongodb://localhost:27017/test';
const opts = { useNewUrlParser: true };

(async function() {

  try {

    const conn = await mongoose.connect(uri, opts);

    // get your 'data' from somewhere of course.

    let result = await WIP.bulkWrite(
      data.map(wip => 
       ({
          updateOne: {
            filter: query_based_on_wip_values
            update: update_based_on_wip_values,
            upsert: true
          }
       })
     )
   );

  } catch (e) {
    console.error(e);
  } finally {
    mongoose.disconnect();
  }

})()

И это, конечно, требует только один запрос к серверу с один ответ для всего содержимого в массиве. Если массив особенно велик, вы можете разбить его на части. Но опять же, для «большого массива» вы должны загружать данные по частям, чтобы не все они были в массиве.

В целом, выберите один структурный шаблон и придерживайтесь его, и найдите время, чтобы понять методы API и то, что они делают. Обычно find() затем изменяют в коде, а шаблон save() - это очень плохая практика , и в основном вводит дополнительные накладные расходы на запросы назад и вперед, плюс очевидная проблема, заключающаяся в том, что прочитанные вами данные, возможно, изменились другим процессом / обновить к тому времени, когда вы решите написать его обратно.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...