Mongoose 中未定义 “new” 操作未及时返回

· 约 5 分钟读完

Mongoose 是 Node.js 中一个非常流行的 ODM 框架,可以方便地在 Node.js 中操作 MongoDB 数据库。然而,有时在编写 Mongoose 代码时可能会遇到以下问题:当定义了新的 Schema (即数据模型),仍然无法及时返回 ”new“ 操作结果。本文将详细介绍该问题的原因,并提供解决方案。

问题描述

在 Mongoose 中使用 Schema 进行模型定义后,一般使用模型创建新文档的操作包括如下两个步骤:

  1. 定义一个新的数据模型;
  2. 调用该模型的 ”new“ 操作,并传入需要创建的新文档数据,完成新文档创建。

一般情况下,这两步操作都能够顺利进行,并且”new“ 操作也能够及时返回,返回新创建的文档实例。但是,有时候你会发现即使已经创建了一个新模型,但是调用 ”new“ 操作之后,它并不会及时返回新文档实例。这是一个异常现象,需要进行处理。

原因分析

造成此问题的原因是因为 Mongoose 在初始化 Schema 的时候,会对其中定义的成员进行如下分类:

  • 模型方法(例如创建文档的方法、静态方法等);
  • 实例方法(例如通过一个文档实例来获取或修改文档内容的方法);
  • 虚拟属性(不对应数据库内字段的计算属性)。

对于上述三种成员中的任意一种,Mongoose 都会在初始化完 Schema 之后将其编译为相应的原型方法或者静态方法,并将这些方法挂载到 Model 或者文档实例的原型中。

然而,在 Schema 初始化过程中可能存在一些异步操作,比如在一个中间件的回调函数中调用了异步方法,或者在插件中定义的函数是异步函数。这些异步操作没有被正确地处理,导致在 Mongoose 加载时并不能正确地编译这些方法,从而无法按照预期地运行 ”new“ 操作,不会及时返回新文档的实例。

解决方案

针对以上问题,我们可以通过以下几种方式解决:

1. 及时调用异步操作

如果你确定某个异步操作对于新建文档的初始化是必须的,那么要及时对其进行调用。

上述代码中,我们新建了一个 Schema,并在其中定义了一个异步操作,该操作需要在文档存储前的附加处理中执行。因为内部 setTimeout 函数是异步的,所以在执行 new TestModel({ title: 'mock' }) 时会被阻塞,无法及时返回新文档实例。

我们需要在异步处理结束后,显式地调用 next 函数才能使”new“操作返回。因为 next 函数是一个回调函数,无法直接控制它何时执行,所以在这里通过 setTimeout 函数模拟了一个异步调用,以模拟真实场景下的异步操作。

2. 使用 Promise

可以用 Promise 标准化处理方式,以便在异步操作完成时即时返回 Promise 实例。在 Promise 的 then 回调中,能够及时获知 “new” 操作创建的新文档实例。

上述代码中,我们依然是新建了一个类似的 Schema,并在其中定义了一个异步操作方法。不同的是,在”new“操作中我们将回调函数中的 next 方法注释掉,代替它的是用 Promise 函数封装了异步调用,并在 then 回调中完成异步操作的异步响应,确保”new“操作能够及时返回新文档实例。

3. 使用 async 和 await

如果你熟悉 ES6 中的 async 和 await,也可以用它们封装”new“操作。在 await 回调中操作完成后,可以及时地返回新的文档实例。

上述代码中,我们依然是新建了一个类似的 Schema,并在其中定义了一个异步操作方法。不同的是,我们使用 async 和 await 来封装 ”new“ 操作,在 async 中使用 await 返回异步处理完成的值,完成异步操作的异步响应,确保生成文档实例的及时返回。

小结

由于 Mongoose 在初始化 Schema 过程中可能存在异步操作,导致在 ”new“ 操作中可能无法及时返回对应的文档实例。对于这种情况,我们可以及时调用异步操作、使用 Promise 或者使用 async 和 await 进行封装,以确保能够及时返回新文档实例。

本文提到了 Mongoose 的一些错误和解决方法,对于有Mongoose 的开发者有指导和帮助作用。

来源:JavaScript中文网 ,转载请注明来源 https://www.javascriptcn.com/post/67d03d7be46428fe9ed0329d