使用 ES6 Proxy 延迟加载资源

Raf*_*deh 6 javascript metaprogramming mongodb node.js es6-proxy

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

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

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

    // 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();                            
    
    Run Code Online (Sandbox Code Playgroud)
    // 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();     
    
    Run Code Online (Sandbox Code Playgroud)

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

Tom*_*Tom 2

算术不能异步

您可能可以从 getter 中启动异步读取(我没有尝试过,但它似乎合法),但 getter 无法等待结果。因此,除非您的数据库库提供了一些阻塞order.weight访问调用,否则及时获取的这一行以及乘法中使用的值在任何惰性读取机制中都将始终是纯粹的幻想:

order.shipmentCost = order.weight * 4.5
Run Code Online (Sandbox Code Playgroud)

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

无法对 Promise 进行乘法运算。无法等待本身不是异步的异步值。即使不是严格异步/等待的事件,也需要一些异步外观回调模式,这两者都不是阻塞的,因此两者都不能使该语句起作用。

可能有效,但它迫使每个调用者管理延迟加载:

order.shipmentCost = (await order.weight) * 4.5
Run Code Online (Sandbox Code Playgroud)

这种方法将会改变你的整个生态系统。read对于调用者来说,在需要时简单地调用&会更好save

或者,您也许能够创建一个在 getter 内部工作的生成器,但您仍然需要为每个属性的第一次访问显式地“启动泵”,这将使“幻想”语句起作用,但代价是产生可怕的 pre - 声明是await相反的。再次强调,最好只使用readsave


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

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


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

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

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