首页 > 解决方案 > 为什么 findOneAndRemove 有时会在回调中将 null 传递给 doc?

问题描述

我正在使用以下应该具有回调输入的函数: https ://mongoosejs.com/docs/api.html#model_Model.findOneAndRemove

我有以下 Mutation 对象,该对象应该从数据库中删除特定药物,然后从包含该 ID 的所有 Day 条目的数组中删除其 MedicineId 的所有实例。

 deleteMedicine: {
            type: MedicineType,
            args: {
                id: { type: new GraphQLNonNull(GraphQLID) }
            },

            async resolve (parent, args) {

                let res = Medicine.findOneAndRemove({ _id: args.id }, async (err, doc) => {

                    if (err === null && doc === null || doc === null) {
                        return;
                    } else if (err) {
                        console.error(err);
                        return;
                    } else {
                        return await Promise.all(doc.dayNames.map(dayName => {
                            return DayOfWeek.findByIdAndUpdate(dayName, { $pull: { medicineIds: doc._id }})
                            .catch((error1) => console.error(error1));
                        })).catch((error2) => console.error(error2)); 
                    }
                });

                await res; 
                return res; 
            }
        }

findOneAndRemove 成功删除了 Medicine 集合中具有 args.id 的文档,但是当它调用回调函数时,有时会将 null 传递给 doc,因此回调函数无法始终正确执行。

即使我发现了所有错误,我也会收到未处理的错误警告。\

根据这篇文章,我添加了当 err 和 doc 都为空时处理的逻辑: https ://github.com/Automattic/mongoose/issues/5022

标签: javascriptnode.jsasync-awaitgraphql-js

解决方案


你应该只对 GraphQL.js 使用 Promises,而不是回调。如果您使用的库不支持 Promise,那么您需要将每个回调包装在 Promise中。但是,mongoose它有很好的 Promise 支持——您只需从方法调用中省略回调,调用将返回一个 Promise。

为了避免在使用 Promises 时出现“回调地狱”以及正确链接 Promises 的问题,通常最好使用 async/await 语法。您清理后的解析器看起来像:

async resolve (parent, args) {
  // Omit the callback to get a Promise and then await the result
  const medicine = await Medicine.findOneAndRemove({ _id: args.id })

  // If a medicine with that ID couldn't be found, we'll get a null
  if (medicine) {
    // Make sure we await the Promise returned by Promise.all 
    await Promise.all(doc.dayNames.map(dayName => DayOfWeek.findByIdAndUpdate(
      dayName,
      { $pull: { medicineIds: doc._id } }
    ))
  }

  // Make sure we return the appropriate result depending on the field's type
  return medicine
}

通常在处理 Promises 时,我们使用该catch方法(或 try/catch 与 async/await)来处理错误。但是,如果您想屏蔽或以其他方式格式化您的错误,这仅在解析器中是必需的——否则,GraphQL 会很乐意为您捕获任何错误并在响应中返回它们。但是,这仅在您的 Promise 正确链接并且解析器返回 Promise 时才有效。如果你最终得到一个没有等待的“孤儿” Promise,你会看到未处理的错误警告。


推荐阅读