我正在尝试创建一个NodeJS应用。下面是管理员创建新产品时应该调用的代码。大多数代码都可以工作,但是只有在执行其余代码(DB函数是异步的)之后才能渲染视图。我已将许多代码包含在promises中(以使某些块以正确的顺序执行)和控制台日志(以查明问题)。
我想指出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没有被识别为函数。我正在使用Node 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.");
})
});
}
答案 0 :(得分:1)
一些提示:如果你的功能超过100行,你可能会在功能上做很多事情。
如果您必须以获得产品的方式从您的请求中获取数据,那么请编写更好的客户端代码(产品应该是一系列不需要消毒的产品对象)。服务器上需要验证,因为您永远不会信任客户端发送给您的内容。但是,查看清理,您甚至不相信您的客户端脚本会向您发送信息。
尝试编写一些小功能并尝试使用它们。
使用.map
将类型a映射到类型b(例如req.body到产品数组,如示例代码中所示)。
使用.map
的结果作为Promise.all
使用util.promisify
将回调函数更改为返回承诺的函数,因为您使用旧版本的节点我已经添加了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)
)
});
}