javascript - 重构 Promise javascript 代码

标签 javascript node.js mongoose

基本上,我有一个代码可以通过将用户输入的优惠券应用到订单交易中来批准订单代码:

var Order = require('../models/order');
var Product = require('../models/product');
var Coupon = require('../models/coupon');

var _ = require('lodash');

exports.approveOrder = function(req, res) {
    var tempProducts;    
    var tempCoupon;
    var orderNbr = req.params.OrderNbr;
    if (orderNbr != undefined && orderNbr != '')
    {
        Order.findOne({ OrderNbr: orderNbr }).exec()
        .then(function(order) {
            if (order == undefined) {
                throw "Order " + orderNbr + " does not exist.";
            }
            else {
                if (order.OrderLines.length <= 0) throw "Ensure your Order has at least one order line.";

                if (req.body.CouponCode != undefined && req.body.CouponCode != '') {
                    // ***********************************************
                    // Want to refactor this following codes into some functions
                    // ***********************************************                  
                    Coupon.findOne({ CouponCode: req.body.CouponCode }).exec()
                    .then(function(coupon) {                        
                        if (coupon == undefined) {
                            throw "Coupon " + req.body.CouponCode + " does not exist.";
                        }
                        else {
                            if (coupon.Qty > 0 && (coupon.ValidFrom <= new Date() && coupon.ValidTo >= new Date())) {
                                coupon.Qty -= 1;
                                tempCoupon = coupon;

                                var prodNbrs = _.map(order.OrderLines, 'ProdNbr');
                                return Product.find({ ProdNbr: { $in : prodNbrs } }).exec();
                            }
                            else {
                                throw "Coupon " + coupon.CouponCode + " is not valid.";
                            }
                        }
                    })
                    .then(function(products) {
                        var prodNbrs = _.map(order.OrderLines, 'ProdNbr');                                                
                        var totalQtyPerProd = 0;

                        _.forEach(products, function(product) {
                            totalQtyPerProd = _.sumBy(order.OrderLines, function(line) {
                                if (product.ProdNbr == line.ProdNbr) return line.Qty;
                            })
                            if (product.QtyOnHand - totalQtyPerProd < 0) throw "Product " + product.ProdNbr + " has insufficient quantity on hand.";

                            _.remove(prodNbrs, function(nbr) { return nbr == product.ProdNbr });
                            product.QtyOnHand -= totalQtyPerProd;

                            var totalDiscount = tempCoupon.Value / order.OrderLines.length;
                            if (tempCoupon.IsPercentage) {
                                totalDiscount = 1 - (tempCoupon.Value / 100);
                            }

                            _.forEach(order.OrderLines, function(line) {
                                if (line.ProdNbr == product.ProdNbr) {
                                    line.UnitPrice = product.UnitPrice;

                                    line.Amount = (line.Qty * line.UnitPrice) - totalDiscount;
                                    if (line.Amount < 0) line.Amount = 0;
                                    if (tempCoupon.IsPercentage) {
                                        line.Amount = (line.Qty * line.UnitPrice) * totalDiscount;
                                    }
                                }
                            })
                        })

                        if (prodNbrs.length > 0) throw "Product " + prodNbrs[0] + " does not exist.";

                        tempProducts = products;

                        order.CouponCode = tempCoupon.CouponCode;
                        order.Status = 'S';
                        return order.save();
                    })
                    .then(function() {
                        return tempCoupon.save();
                    })
                    .then(function() {
                        _.forEach(tempProducts, function(product) {
                            product.save()
                            .then(function() {

                            })
                            .catch(function(err) {
                                if (err) res.status('500').jsonp({ error: err });
                            });
                        })
                        res.status('200').jsonp({ information: "Order "+ order.OrderNbr +" has been submitted successfully." });
                    })
                    .catch(function(err) {
                        if (err) res.status('500').jsonp({ error: err });
                    });
                }
            }            
        }) 
        .catch(function(err) {
            if (err) res.status('500').jsonp({ error: err });
        });
    }
    else {
        res.status('500').jsonp({ error: "Order Number must be specified." });
    }
};

我想将每个“then”子句的代码拆分为一些函数:

  1. 找到优惠券,然后返还优惠券。
  2. 更新订单行金额
  3. 更新优惠券
  4. 返回确认消息

我尝试使用局部变量来保留找到的优惠券,不幸的是,如果它超出了 findOne() 方法,该变量将是未定义的,所以在这段代码中我在 findOne() 中使用了很多又长的“.then”

有什么想法吗?

最佳答案

您使用“then chain”的方法很好。 我将重构代码,创建单独的函数以在链中使用,如下所示:

var saveProducts = function() {
                        _.forEach(tempProducts, function(product) {
                            product.save()
                            .then(function() {

                            })
                            .catch(function(err) {
                                if (err) res.status('500').jsonp({ error: err });
                            });
                        })
                        res.status('200').jsonp({ information: "Order "+ order.OrderNbr +" has been submitted successfully." });
                    }

var saveCoupon = function() {
                        return tempCoupon.save();
                    }

var products = function(products) {
                        var prodNbrs = _.map(order.OrderLines, 'ProdNbr');                                                
                        var totalQtyPerProd = 0;

                        _.forEach(products, function(product) {
                            totalQtyPerProd = _.sumBy(order.OrderLines, function(line) {
                                if (product.ProdNbr == line.ProdNbr) return line.Qty;
                            })
                            if (product.QtyOnHand - totalQtyPerProd < 0) throw "Product " + product.ProdNbr + " has insufficient quantity on hand.";

                            _.remove(prodNbrs, function(nbr) { return nbr == product.ProdNbr });
                            product.QtyOnHand -= totalQtyPerProd;

                            var totalDiscount = tempCoupon.Value / order.OrderLines.length;
                            if (tempCoupon.IsPercentage) {
                                totalDiscount = 1 - (tempCoupon.Value / 100);
                            }

                            _.forEach(order.OrderLines, function(line) {
                                if (line.ProdNbr == product.ProdNbr) {
                                    line.UnitPrice = product.UnitPrice;

                                    line.Amount = (line.Qty * line.UnitPrice) - totalDiscount;
                                    if (line.Amount < 0) line.Amount = 0;
                                    if (tempCoupon.IsPercentage) {
                                        line.Amount = (line.Qty * line.UnitPrice) * totalDiscount;
                                    }
                                }
                            })
                        })

                        if (prodNbrs.length > 0) throw "Product " + prodNbrs[0] + " does not exist.";

                        tempProducts = products;

                        order.CouponCode = tempCoupon.CouponCode;
                        order.Status = 'S';
                        return order.save();
                    }

var getProducts = function(coupon) {                        
                        if (coupon == undefined) {
                            throw "Coupon " + req.body.CouponCode + " does not exist.";
                        }
                        else {
                            if (coupon.Qty > 0 && (coupon.ValidFrom <= new Date() && coupon.ValidTo >= new Date())) {
                                coupon.Qty -= 1;
                                tempCoupon = coupon;

                                var prodNbrs = _.map(order.OrderLines, 'ProdNbr');
                                return Product.find({ ProdNbr: { $in : prodNbrs } }).exec();
                            }
                            else {
                                throw "Coupon " + coupon.CouponCode + " is not valid.";
                            }
                        }
                    }

exports.approveOrder = function(req, res) {
    var tempProducts;    
    var tempCoupon;
    var orderNbr = req.params.OrderNbr;
    if (orderNbr != undefined && orderNbr != '')
    {
        Order.findOne({ OrderNbr: orderNbr }).exec()
        .then(function(order) {
            if (order == undefined) {
                throw "Order " + orderNbr + " does not exist.";
            }
            else {
                if (order.OrderLines.length <= 0) throw "Ensure your Order has at least one order line.";

                if (req.body.CouponCode != undefined && req.body.CouponCode != '') {
                    // ***********************************************
                    // Want to refactor this following codes into some functions
                    // ***********************************************                  
                    Coupon.findOne({ CouponCode: req.body.CouponCode }).exec()
                    .then(getProducts)
                    .then(products)
                    .then(saveCoupon)
                    .then(saveProducts)
                    .catch(function(err) {
                        if (err) res.status('500').jsonp({ error: err });
                    });
                }
            }            
        }) 
        .catch(function(err) {
            if (err) res.status('500').jsonp({ error: err });
        });
    }
    else {
        res.status('500').jsonp({ error: "Order Number must be specified." });
    }
};

每个函数的结果将传递到链中的下一个函数

我建议这个关于 Promise 最佳实践的帖子

https://github.com/airbnb/javascript/issues/216

关于javascript - 重构 Promise javascript 代码,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/47034622/

相关文章:

javascript - Node websocket-server -- ETIMEDOUT 错误

javascript - "Cannot read property ' id ' of undefined"解析在工厂中声明的数组时

node.js - 比较 Mongoose 查询中的 ObjectID

node.js - MongoDB/ Mongoose : find(query) and aggregate([ $match: query ]) results differ when using dates and $gte/$lte

javascript - 如何在 JavaScript 中创建对象集合并在另一个对象中赋值?

javascript - 根上的 Spring RequestMapping

javascript - 如何设置 Controller ,以便根据选择的按钮执行特定命令?

node.js - Axios POST 表单数据在服务器端有空正文

node.js - GAE 上的 Nodejs Websocket

node.js - Node Express APP 1 到 N(带 MongoDB)