节点-异步循环未等待会导致Mongo过早断开连接

时间:2019-04-18 23:34:59

标签: javascript node.js mongodb async-await

我有一个要解析的对象数组。我检查Mongo以查看记录是否存在条目。如果是这样,我将更新记录。否则,我创建记录。一切正常。但是,在循环过程之后,我想执行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 for(let wip of array)和for(let wip in array)。为什么诺言立即返回?

2 个答案:

答案 0 :(得分: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.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 :(得分:1)

为什么只用一种标准方式将async/await与标准Promise语法混合使用呢?同样,所有猫鼬方法无论如何都返回一个Promise,所以我不明白为什么您甚至有任何尝试用Promise包装回调的东西。

清单基本上显示了对Promises和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(即本机猫鼬方法result),并确保您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()模式是真正的坏习惯,并且基本上引入了来回请求的额外开销,以及您读取的数据存在的明显问题您决定将其写回时,可能已被另一个进程/更新更改。