乐闻世界logo
搜索文章和话题

How to work with async code in Mongoose virtual properties?

4 个月前提问
3 个月前修改
浏览次数14

2个答案

1
2

在Mongoose中,虚拟属性通常用于获取关于文档的信息,而这些信息并不直接保存在数据库中。虚拟属性是非常灵活的,但它们默认情况下是同步的。如果你需要在虚拟属性中执行异步操作,例如从另一个服务获取数据,那么需要采取一些特别的方法来实现。

使用方法

  1. 定义一个实例方法而不是虚拟属性: Mongoose中的虚拟属性不支持异步操作,但你可以使用实例方法来达到类似的效果。实例方法可以是异步的,从而允许你执行数据库查询或其他异步操作。

    例子: 假设有一个User模型,我们需要计算用户的年龄,而出生日期是从另一个API异步获取的。

    javascript
    const mongoose = require('mongoose'); const Schema = mongoose.Schema; const userSchema = new Schema({ name: String, dob: Date, // 出生日期 }); // 添加一个异步的实例方法来获取年龄 userSchema.methods.calculateAge = async function() { const today = new Date(); const birthDate = new Date(this.dob); let age = today.getFullYear() - birthDate.getFullYear(); const m = today.getMonth() - birthDate.getMonth(); if (m < 0 || (m === 0 && today.getDate() < birthDate.getDate())) { age--; } return age; }; const User = mongoose.model('User', userSchema); // 使用实例方法 async function getAge(userId) { const user = await User.findById(userId); const age = await user.calculateAge(); console.log(`The age of the user is ${age}`); }
  2. 使用虚拟属性的get方法配合其他方式: 虽然虚拟属性自身不支持异步操作,你可以在虚拟属性的get方法中返回一个已经解析的值,这个值可以在其他地方异步设置。

    例子: 我们仍然用上面的User模型,但这次我们会在用户实体中预先加载年龄。

    javascript
    userSchema.virtual('age').get(function() { return this._age; }); userSchema.methods.loadAge = async function() { this._age = await this.calculateAge(); }; const user = new User({ name: 'Alice', dob: '1990-05-15' }); await user.loadAge(); // 异步加载年龄 console.log(user.age); // 使用虚拟属性

总结

虽然Mongoose的虚拟属性不直接支持异步操作,但通过实例方法或者结合其他属性和方法,我们可以有效地实现异步处理的需求。这样既保持了代码的清晰性,也利用了Mongoose的强大功能。

2024年8月24日 16:39 回复

在Mongoose中,虚拟属性通常用于处理不需要保存到MongoDB数据库的属性,但需要根据其他属性动态计算的值。虚拟属性默认情况下是同步的,但是您可以通过在虚拟属性的getter函数中返回一个Promise来实现异步操作。这使得在计算属性值时能够执行异步操作,如数据库查询或调用外部API。

示例

假设我们有一个用户模型,其中包含用户的出生日期,我们需要计算用户的年龄,但年龄是根据当前日期动态计算的,所以我们计划使用虚拟属性来实现。

javascript
const mongoose = require('mongoose'); const Schema = mongoose.Schema; const userSchema = new Schema({ name: String, birthdate: Date, }); // 使用异步的getter userSchema.virtual('age').get(function() { return new Promise((resolve, reject) => { try { const today = new Date(); const birthDate = new Date(this.birthdate); let age = today.getFullYear() - birthDate.getFullYear(); const m = today.getMonth() - birthDate.getMonth(); if (m < 0 || (m === 0 && today.getDate() < birthDate.getDate())) { age--; } resolve(age); } catch (error) { reject(error); } }); }); const User = mongoose.model('User', userSchema); async function getAgeOfUser(userId) { try { const user = await User.findById(userId); const age = await user.age; // 调用虚拟属性获取年龄 console.log(`The age of the user is ${age}`); } catch (error) { console.error('Failed to retrieve user age:', error); } } // 假设我们有一个用户ID来测试 getAgeOfUser('someUserId');

注意事项

  1. 性能考虑:在虚拟属性中使用异步操作可能会影响应用程序的性能,尤其是当异步操作涉及网络请求或复杂计算时。务必确保这些操作是必要的,并考虑缓存或其他优化措施。

  2. 错误处理:在上面的例子中,我们在Promise中处理了错误。这是必要的,因为如果不处理错误,可能会导致运行时问题。

  3. 使用场景:通常,如果可以避免在虚拟属性中进行异步操作,这将是理想的选择,因为它可能会使得模型的使用变得复杂。如果只是需要做一些简单的计算或格式转换,应该继续使用同步的虚拟属性。

通过这种方式,虚拟属性可以灵活地包含复杂的逻辑,同时仍然保持模型的清晰和高效。

2024年8月24日 16:40 回复

你的答案