Node - Async for Loop не ожидает, что Mongo отключается слишком рано

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

mongoose.set('useCreateIndex', true);
  mongoose
    .connect(db, { useNewUrlParser: true })
    .then(() => console.info('MongoDB Connected'))
    .then(async () => {
      await parseWIP(converted);
      mongoose.disconnect();
    })
    .catch(err => console.info(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.info(`Updated WIP`);
            });
          } else {
            // Create new record
            await createNewWip(wip);
          }
        })
        .catch(err => {
          console.info(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.info(savedWipRecord._id);
      })
      .catch(err => reject(err));
    resolve();
  });
}

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

Почему async + await внутри then? Это все Обещания. ...then(() => parseWIP(data)).then(() => mongoose.disconnect()).catch...

tehhowch 19.04.2019 01:44

Я пробовал это изначально, и он сразу отключается. mongoose .connect(db, {useNewUrlParser: true}) .then(() => console.info('MongoDB Connected')) .then(() => {parseWIP(converted); }) .then(() = > mongoose.disconnect()) .catch(ошибка => console.info(ошибка));

Andrew Taylor 19.04.2019 02:00

Посмотрите мой ответ, хотя иметь и then, и await неудобно, проблема в том, что вы не используете возврат ожидаемого Promise

Jack 19.04.2019 02:02

Используйте Promise.all вместо for (let wip of array). Цикл for откладывает связанный метод findOne и разрешается после запуска этих методов.

tehhowch 19.04.2019 02:45
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
В настоящее время производительность загрузки веб-сайта имеет решающее значение не только для удобства пользователей, но и для ранжирования в...
Безумие обратных вызовов в javascript [JS]
Безумие обратных вызовов в javascript [JS]
Здравствуйте! Юный падаван 🚀. Присоединяйся ко мне, чтобы разобраться в одной из самых запутанных концепций, когда вы начинаете изучать мир...
Система управления парковками с использованием HTML, CSS и JavaScript
Система управления парковками с использованием HTML, CSS и JavaScript
Веб-сайт по управлению парковками был создан с использованием HTML, CSS и JavaScript. Это простой сайт, ничего вычурного. Основная цель -...
JavaScript Вопросы с множественным выбором и ответы
JavaScript Вопросы с множественным выбором и ответы
Если вы ищете платформу, которая предоставляет вам бесплатный тест JavaScript MCQ (Multiple Choice Questions With Answers) для оценки ваших знаний,...
0
4
910
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Ответ принят как подходящий

Если вы хотите вызвать 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.info(`Existing WIP: ${existingWip.id}`);
                  ... assign some values ...
                  existingWip.lastModified = moment.now();

                  await existingWip.save().then(updatedWip => {
                    console.info(`Updated WIP ${updatedWip.id}`);
                  });
                } else {
                  // Create new record
                  await createNewWip(wip);
                }
              })
              .catch(err => {
                  console.info(err);
                  reject(err);
                });
        }  
        console.info('here we are at end of loop'); 
        resolve();
    });
}

Вы отключаетесь сразу после await parseWIP(data);?

Jack 19.04.2019 02:05

Если вы пытались вернуть промис в функцию, а он все равно немедленно возвращается, это означает, что что-то внутри функции не ожидается должным образом.

Jack 19.04.2019 02:07

Я думаю, вы забыли дождаться операции WIP.findOne.

Jack 19.04.2019 02:39

Зачем смешивать async/await со стандартным синтаксисом Promise, если можно просто написать все одним стандартным способом. Кроме того, все методы мангуста в любом случае возвращают 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.info(`Existing WIP: ${existingWip.id}`);
        ... assign some values ...
        existingWip.lastModified = moment.now();

        let updatedWip = await existingWip.save()
        console.info(`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 (то есть результат собственного метода мангуста) и что вы 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.info(`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() имеет вид действительно плохая практика и в основном вводит дополнительные накладные расходы на запросы туда и обратно, а также очевидную проблему, заключающуюся в том, что данные, которые вы читаете, могут быть изменены другим процессом/обновлением к тому времени, когда вы решите записать их обратно.

Другие вопросы по теме