使用 ES6 Proxy 延迟加载资源

问题描述

我正在为存储在 MongoDB 中的文档(类似于 Mongoose)构建一个类似于 ActiveRecord 类的东西。我有两个目标:

  1. 使用代理拦截文档上的所有属性设置器,并自动创建要发送到 Mongo 的更新查询。我已经在 SO 上找到了解决此问题的 a solution

  2. 防止从数据库中进行不必要的读取。 IE。如果在文档上执行一个函数,并且该函数设置属性,并且从不使用文档的现有属性,那么我不需要从数据库中读取文档,我可以直接更新它。但是,如果该函数使用文档的任何属性,我必须先从数据库中读取它,然后才能继续使用代码。示例:

    // Don't load the document yet,wait for a property 'read'.
    const order = new Order({ id: '123abc' });
    // Set property.
    order.destination = 'USA'; 
    // No property 'read',Order class can just directly send a update query to Mongo ({ $set: { destination: 'USA' } }).      
    await order.save();                            
    
    // Don't load the document yet,wait for a property 'read'.
    const order = new Order({ id: '123abc' });
    // Read 'weight' from the order object/document and then set 'shipmentCost'.
    // Now that a 'get' operation is performed,Proxy needs to step in and load the document '123abc' from Mongo.
    // 'weight' will be read from the newly-loaded document.
    order.shipmentCost = order.weight * 4.5;     
    await order.save();     
    

我该怎么做?这似乎很简单:在文档对象上设置一个“get”陷阱。如果它是第一个属性“get”,则从 Mongo 加载文档并缓存它。但是如何将异步操作放入 getter 中?

解决方法

算术不能异步

您可能可以从 getter 中启动异步读取(我还没有尝试过,但它看起来是合法的),但是 getter 不能等待结果。因此,除非您的数据库库提供了一些阻塞访问调用,否则这一行(其中 order.weight 是及时获取并用于乘法的值)在任何情况下都将永远是纯幻想 懒惰阅读机制:

order.shipmentCost = order.weight * 4.5

(如果您的数据库库确实有阻塞读取,我认为通过仅使用阻塞读取来构建您想要的内容会很简单。尝试一下。我认为这是 Sequelize 的 dataLoader 所做的一部分.)

乘法无法对 Promise 进行操作。没有办法等待一个本身不是异步的异步值。即使不是严格意义上的 async/await 的 Events 也需要一些异步外观回调模式,它们都不是阻塞的,因此它们都不能使该语句起作用。

可以工作,但它强制每个调用者管理延迟加载:

order.shipmentCost = (await order.weight) * 4.5

这种方法会使您的整个生态系统变形。调用者在需要时简单地调用 readsave 会好得多。

或者你可以创建一个在 getter 中工作的生成器,但你仍然需要为每个属性的第一次访问明确地“启动泵”,这将使“幻想”语句以生成为代价工作一个可怕的预声明 await 代替。同样,最好只使用 readsave


我认为您所希望的在 javascript 中是不可能的,因为阻塞和非阻塞行为是不透明的,无法实现。任何异步机制最终都会表现为非标量。

您需要创建自己的预编译器,如 JSX,它可以将奇幻代码转换为异步/感知垃圾。


严肃的建议:使用现成的持久性库,而不是自己开发

  1. 数据持久化的问题空间充满了许多非常困难的问题和边缘情况。您需要解决的问题比您想象的要多。
  2. 除非您的整个项目都是“构建更好的持久性技术”,否则您不会构建比现有技术更好的东西,这意味着构建自己的项目只是获得持久性的最慢方法劣质的解决方案。
  3. 您编写的代码越多,需要修复的错误就越多。 (您正在为这个神奇的持久性库编写测试,对吗?)

如果您正在尝试构建一个真正的应用程序并且您只需要与 Mongo 交互,请花 15 分钟在 npm 上购物并继续。人生如此短暂。没有人会关心你的手工制作的数据库层有多“酷”,它几乎就像 ActiveRecord(除了一些固执的自定义错误缺失的功能-- 所有这些都会成为他人甚至自己的障碍)。