javascript - NodeJS+Mongoose - 需要帮助使用 Promises 重构代码

标签 javascript node.js mongoose promise

考虑这个例子:

router.post('/', function (req, res, next) {

var order = new Order({

    customer_id: req.body.customer_id,
    order_elements: []
});

order.save(function(err, saved_order) {

    var elementsSaveCount = 0;

    req.body.order_elements.forEach( function(order_element, index) {

        orderElement = new OrderElement({

            order_id: saved_order._id,
            name: order_element.name
        });

        orderElement.save(function(err, result) {

            elementsSaveCount++;
            order.order_elements.push(result._id);

            if(elementsSaveCount >= req.body.order_elements.length) {

                saved_order.save();
                res.status(201).json({
                message: 'order saved',
                order: order
                });
            }
        });
    });
});
});

“Order”模型有一个 id 为“OrderElements”的数组。 首先,我保存订单,然后在 save() 的回调中循环访问请求的 OrderElements 以保存它们,并将每个 id 推送到订单内。 我使用变量“elementsSaveCount”来检查所有 save() 何时执行。

我想使用 Promise 重构此代码。

最佳答案

严格来说,如果我实际上重构给定的代码示例,这并不是我会做的事情,但我将尝试说明 Promises 将帮助我们重构处理程序的几种不同方式:

router.post('/', (req, res, next) => {
  const saveOrdersWithParent = parentOrderId => req.body.order_elements
  .map(order_element =>
    new OrderElement({
      order_id: parentOrderId, name: order_element.name
    }).save()
  );

  return new Order({
    customer_id: req.body.customer_id,
    order_elements: []
  }).save()
  .then(saved_order =>
    Promise.all(saveOrdersWithParent(saved_order._id))
    // if you want to catch invidiually failed items separately,
    // otherwise omit this and the next catch will get it
    .catch(err => {
      console.error('Some individual order element could not be saved', err);

      // Important: we need to "bubble up" the error if we want to handle it
      // specially or else the next 'then' will go ahead and run but of
      // course it's `orders` param will be undefined.
      // Usually avoid this situation but it's good to know
      throw err;
    }).then(orders => {
      // assuming you wanted this to be persisted to the database
      // and not just the local representation
      saved_order.order_elements = orders.map(o => o._id);
      return saved_order.save();
    })
  ).then(order => res.status(201).json({ message: 'order saved', order }))
  .catch(err => console.error('Original order could not be saved', err));
});

首先,Promise 的好处是它们非常容易编写,因为您可以轻松地将它们视为值。因此,您会注意到我将一些有点困惑的逻辑(创建和保存订单元素)分解为一个返回 Promise 数组的辅助函数。不是非常必要,但它是帮助清理 promise 链的好工具。使用 Promises 使复杂的异步代码更清晰时,真正关键的是使链本身尽可能裸露,争取像这样的东西:createPost().then(persistPost).then(notifyUsersOfPost).then(etc)高层发生的事情非常清楚,但隐藏了细节。您也可以通过回调来做到这一点,但这要困难得多。

Promise.all可用于等待所有订单元素成功保存,从而无需计数器。如果任何订单元素未成功保存,它将直接跳至 ​​catch block ,您可以在其中处理某些订单元素未保存的情况。仅当您可以通过某种方式恢复时才真正执行此操作,也许可以通过重试(请参阅代码示例中的注释)。

所以我们这里确实有一些嵌套,比如回调,但要少得多。通常,您希望在嵌套的 Promise 中进行操作,作为显示对象“生命周期”的一种方式。在此示例中,嵌套的 Promise(保存订单元素)需要原始保存订单的引用,因此我们可以非常清楚地看到代码的哪些部分依赖于该(中间)值。当我们不需要那个的时候 saved_order我们将值(value)回溯到顶层 promise 链。同样有效的事情可能是这样的:

.then(saved_order => {
  const orderSaves = saveOrdersWithParent(saved_order._id)
    .catch(err => {
      console.error('Some individual ...', err);
      throw err;
    })
  return Promise.all([ saved_order, orderSaves... ]);
}).then(([ saved_order, ...orders ]) => {
  saved_oder.order_elements = orders.map(o => o._id);
  return saved_order.save()
})
...

这可以通过在保存的订单元素旁边包含我们需要的值(持久化的 Order 对象)来避免嵌套,但 IMO 通常不太清楚。

我认为这个特定的示例很好地展示了如何创建操作的“管道”,这些操作被事物需要的输入和生成的内容所阻止。胜利在于清晰的逻辑步骤以及什么取决于什么。

关于javascript - NodeJS+Mongoose - 需要帮助使用 Promises 重构代码,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/48052238/

相关文章:

javascript - 无法将 React Native View 放在底部

javascript - 在 GET 请求的回调中使用 POST 请求(闭包)

javascript - 如何将元素从数组附加到容器?

javascript - 这个下划线在node js中是什么意思

javascript - Mongoose + Mlabs + 海量数据

javascript - 如何在React-redux中状态更新后刷新组件

node.js - 如何在 Electron 中取消注册上下文菜单监听器

node.js - Node 找不到Web3 js

node.js - 如何在 Mongoose/MongoDB 中聚合两个集合?

regex - 为 Mongoose URI 创建正则表达式