簡體   English   中英

節點JS:如何承諾循環

[英]Node JS: How to Promise For Loop

我正在嘗試創建一個NodeJS應用。 以下是管理員創建新產品時應調用的代碼。 大多數代碼都能正常工作,但是只有在其余代碼執行完之后(DB函數是異步的),我才無法呈現視圖。 我已經將許多代碼包裝在Promise(使某些塊按正確的順序執行)和Console日志(以查明問題)中。

我想指出console.log(111)下方的console.dir(rejProducts) console.log(111)日志和空數組。 另外,在for循環的結尾括號之前添加console.dir(rejProducts)記錄一個空數組。 謝謝! 如果您需要更多信息,請告訴我。

app.post('/products/new', function (req, res, next) {
  // Async function: find all categories
  Category.find(function(err, categories) {
    // Hidden count that tells num products to be created by form
    var numProducts = req.body[`form-item-count`];
    // Array of all rejected products adds
    var rejProducts = [];

    var promiseLoopProducts = new Promise(function(resolve, reject) {
      var promiseProducts = [];
      // Loop through all addded products
      for (let i = 0; i < numProducts; i++) {
        var promiseProductCheck = new Promise(function(resolve, reject) {
          var name = validate.sanitize(req.body[`name_${i}`]);
          var category = validate.sanitize(req.body[`category_${i}`]);
          var price = validate.sanitize(req.body[`price_${i}`].replace(/\$/g, ""));
          var stock = validate.sanitize(req.body[`stock_${i}`]);
          var image = validate.sanitize(req.body[`image_${i}`]);
          var description = validate.sanitize(req.body[`description_${i}`]);

          var rejProduct;
          var rejFields = { 'name': name, 'category': category, 'price': price,
                            'stock': stock, 'image': image,
                            'description': description };
          var rejErrors = {};

          var productData = {
            name: name,
            category: category,
            price: price,
            stock: stock,
            image: image,
            description: description
          };
          var promiseCategoryCheck = new Promise(function(resolve, reject) {
            if (ObjectId.isValid(category)) {
              var promiseCategoryCount = new Promise(function(resolve, reject) {
                Category.count({'_id': category}, function(error, count) {
                  rejErrors['name'] = validate.isEmpty(name);
                  if (count == 0) rejErrors['category'] = true;
                  rejErrors['price'] = !validate.isPrice(price);
                  rejErrors['stock'] = !validate.isInt(stock);

                  if( validate.isEmpty(name) || !validate.isPrice(price) ||
                      count == 0 || !validate.isInt(stock) ) {
                    rejProduct = { 'fields': rejFields, 'errors': rejErrors };
                    rejProducts.push(rejProduct);
                    console.dir(rejProducts);
                    console.log(count);
                    return resolve();
                  }
                  else {
                    Product.create(productData, function (error, product) {
                      if (error) return next(error);
                      console.log(77);
                        console.dir(rejProducts);
                      return resolve();
                    });
                  }
                  if (error) return next(error);
                });
              });
              promiseCategoryCount.then(function() {
                console.dir(rejProducts);
                return resolve();
              });
            } else {
              rejErrors['category'] = true;
              rejProduct = { 'fields': rejFields, 'errors': rejErrors };
              rejProducts.push(rejProduct);
              console.dir(rejProducts);
            }
          });
          promiseCategoryCheck.then(function() {
            console.dir(rejProducts);
            promiseProducts.push(promiseProductCheck);
            console.log(promiseProductCheck);
            console.log(promiseProducts);
            return resolve();
          });
        });
        promiseProductCheck.then(function() {
          console.log(106);
          console.dir(rejProducts);
        });
      }
      Promise.all(promiseProducts).then(function() {
        console.log(111);
        console.dir(rejProducts); // Empty array!
        return resolve();
      });
    });

    promiseLoopProducts.then(function() {
      console.log(118);
      console.dir(rejProducts); // Empty array!
      res.render('products/new', { categories: categories, rejProducts: rejProducts });
    });

  });

});

編輯:我已經對代碼進行了一些更改。 似乎util.promisify沒有被識別為函數。 我正在使用節點9.4。

module.exports = function(app){

const util = require('util');
require('util.promisify').shim();
const validate = require('../functions/validate');

const Category = require('../db/categories');
const Product = require('../db/products');

var ObjectId = require('mongoose').Types.ObjectId;
//Category.find as function that returns a promise
const findCategories = util.promisify(Category.find);

const countCategories = (categoryId) => {
  util.promisify(Category.count({'_id': categoryId}));
};

const bodyToProduct = (body, i) => {
  var name = validate.sanitize(body[`name_${i}`]);
  var category = validate.sanitize(body[`category_${i}`]);
  var price = validate.sanitize(body[`price_${i}`].replace(/\$/g, ""));
  var stock = validate.sanitize(body[`stock_${i}`]);
  var image = validate.sanitize(body[`image_${i}`]);
  var description = validate.sanitize(body[`description_${i}`]);
  return {
    name: name,
    category: category,
    price: price,
    stock: stock,
    image: image,
    description: description
  };
};

app.post('/products/new', function (req, res, next) {
  // Async function: find all categories
  return findCategories()
  .then(
    categories=>{
      // Hidden count that tells num products to be created by form
      var numProducts = req.body[`form-item-count`];
      // Array of all rejected products adds
      var rejProducts = [];
      return Promise.all(
        Array.from(new Array(numProducts),(v,i)=>i)
        .map(//map [0...numProducts] to product object
          i=>bodyToProduct(req.body,i)
        )
        .map(
          product => {
            var rejErrors;
            var rejName = validate.isEmpty(name);
            var rejCategory;
            var rejPrice = !validate.isPrice(price);
            var rejStock = !validate.isInt(stock);
            if (ObjectId.isValid(product.category)) {
              return countCategories()
              .then(
                count=> {
                  if (count == 0) rejCategory = true;

                  if(rejName || rejCategory || rejPrice || rejStock ) {
                    rejErrors = {
                      name: rejName,
                      category: rejCategory,
                      price: rejPrice,
                      stock: rejStock
                    }
                    rejProduct = { 'fields': product, 'errors': rejErrors };
                    rejProducts.push(rejProduct);
                    console.dir(rejProducts);
                    console.log(count);
                  } else {
                    Product.create(productData, function (error, product) {
                      if (error) return next(error);
                      console.log(77);
                        console.dir(rejProducts);
                    });
                  }
                }
              ).catch(function() {
                console.log("Count function failed.");
              });
            } else {
              rejCategory = true;
              rejErrors = {
                name: rejName,
                category: rejCategory,
                price: rejPrice,
                stock: rejStock
              }
              rejProduct = { 'fields': product, 'errors': rejErrors };
              rejProducts.push(rejProduct);
              console.dir(rejProducts);
            }
          }
        )
      ).then(function() {
        res.render('products/new', { categories: categories, rejProducts: rejProducts });
      }).catch(function() {
        console.log("Promise all products failed.");
      });
    }
  ).catch(function() {
    console.log("Find categories failed.");
  })
});

}

一些提示:如果您的函數超過100行,則可能在函數中做了很多事情。

如果您必須以獲取請求的方式從請求中獲取數據,請編寫更好的客戶端代碼(產品應該是不需要清理的產品對象數組)。 服務器上需要進行驗證,因為您永遠不會信任客戶端向您發送的內容。 但是,查看清理后,您甚至都不相信客戶端腳本向您發送的內容。

嘗試編寫一些小的功能並嘗試使用這些功能。

使用.map將類型a映射為類型b(例如,將req.body映射到示例代碼中的產品數組)。

.map的結果用作Promise.all的參數

使用util.promisify將回調函數更改為返回promise的函數,因為您使用的是舊版節點,所以我添加了promisify的實現:

var promisify = function(fn) {
  return function(){
    var args = [].slice.apply(arguments);
    return new Promise(
      function(resolve,reject){
        fn.apply(
          null,
          args.concat([
            function(){
              var results = [].slice.apply(arguments);
              (results[0])//first argument of callback is error
                ? reject(results[0])//reject with error
                : resolve(results.slice(1,results.length)[0])//resolve with single result
            }
          ])
        )
      }
    );
  }
};

module.exports = function(app){
  const validate = require('../functions/validate');
  const Category = require('../db/categories');
  const Product = require('../db/products');
  var ObjectId = require('mongoose').Types.ObjectId;
  //Category.find as function that returns a promise
  const findCategories = promisify(Category.find.bind(Category));
  const countCategories = (categoryId) => {
    promisify(Category.count.bind(Category))({'_id': categoryId});
  };
  const createProduct = promisify(Product.create.bind(Product));
  const REJECTED = {};
  const bodyToProduct = (body, i) => {
    var name = validate.sanitize(body[`name_${i}`]);
    var category = validate.sanitize(body[`category_${i}`]);
    var price = validate.sanitize(body[`price_${i}`].replace(/\$/g, ""));
    var stock = validate.sanitize(body[`stock_${i}`]);
    var image = validate.sanitize(body[`image_${i}`]);
    var description = validate.sanitize(body[`description_${i}`]);
    return {
      name: name,
      category: category,
      price: price,
      stock: stock,
      image: image,
      description: description
    };
  };

  const setReject = product => {
    var rejErrors;
    var rejName = validate.isEmpty(product.name);
    var rejCategory;
    var rejPrice = !validate.isPrice(product.price);
    var rejStock = !validate.isInt(product.stock);
    const countPromise = (ObjectId.isValid(product.category))
      ? countCategories()
      : Promise.resolve(0);
    return countPromise
    .then(
      count => {
        if (count == 0) rejCategory = true;

        if (rejName || rejCategory || rejPrice || rejStock) {
          rejErrors = {
            type:REJECTED,
            name: rejName,
            category: rejCategory,
            price: rejPrice,
            stock: rejStock
          }
          return rejErrors;
        }
        return false;
      }
    );
  };

  const productWithReject = product =>
    Promise.all([
      product,
      setReject(product)
    ]);

  const saveProductIfNoRejected = ([product,reject]) =>
    (reject===false)
      ? Product.create(product)
        .catch(
          err=>({
            type:REJECTED,
            error:err
          })
        )
      : reject;

  app.post('/products/new', function (req, res, next) {
    // Async function: find all categories
    return findCategories()
    .then(
      categories => {
        // Hidden count that tells num products to be created by form
        var numProducts = req.body[`form-item-count`];
        // Array of all rejected products adds
        var rejProducts = [];
        return Promise.all(
          Array.from(new Array(numProducts), (v, i) => i)
            .map(//map [0...numProducts] to product object
              i => bodyToProduct(req.body, i)
            )
            .map(
              product=>
                productWithReject(product)
              .then(saveProductIfNoRejected)
            )
        ).then(
          results =>
            res.render(
              'products/new',
              { 
                categories: categories,
                rejProducts: results.filter(x=>(x&&x.type)===REJECTED)
              }
            )
        ).catch(
          error=>
            console.log("Promise all products failed.",error)
        );
      }
    ).catch(
      error=>
        console.log("Find categories failed.",error)
    )
  });
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM