我正在尝试在parse.com上保存不同的食物名称而不重复。但是,当我运行代码时,数据库一遍又一遍地包含相同的2或3种食物,而不是200个左右的独特名称。
以下是我的功能。我尝试在两个不同的点记录食物的名称,我得到不同的值。第一点给出正确的食物名称,但第二点只显示亚麻籽松饼或覆盆子派。我认为这个问题与异步运行的代码有关,但我不知道如何解决这个问题。
Parse.Cloud.define("recordFavorite", function(request, response) {
var foodList = request.params.foodList; //string array of food names
var Food = Parse.Object.extend("Food");
var query = new Parse.Query(Food);
for (i = 0; i < foodList.length; i++ ) {
var name = foodList[i];
console.log("before name is " + name);
var query = new Parse.Query(Food);
query.exists("name", name);
query.find({
success: function(results) {
if(results.length == 0){
var food = new Food();
food.set("name", name);
food.save(null, {
success: function(food) {
console.log("saved with name " + name);
},
error: function(food, error) {
}
});
} else {
//don't create new food
}
},
error: function(error) {
}
});
}
});
我可以通过将其修改为下面粘贴的代码来取得一些进展。现在它保存所有对象,包括重复对象。我注意到了这些行
var query = new Parse.Query(Food);
query.exists("name", name);
返回所有食物的数组,但不会过滤掉包含“name”的对象。 (很明显,这可能仍然出现在原始代码中,但我没有注意到。)
Parse.Cloud.define("recordFavorite", function(request, response) {
var foodList = request.params.foodList; //string array of food names
var foodListCorrected = new Array();
var Food = Parse.Object.extend("Food");
// Wrap your logic in a function
function process_food(i) {
// Are we done?
if (i == foodList.length) {
Parse.Object.saveAll(foodListCorrected, {
success: function(foodListCorrected) {
},
error: function(foodListCorrected) {
}
});
return;
}
var name = foodList[i];
var query = new Parse.Query(Food);
query.exists("name", name);
query.find({
success: function(results) {
console.log(results.length);
if(results.length == 0){
//console.log("before " + foodListCorrected.length);
var food = new Food();
food.set("name", name);
foodListCorrected.push(food);
// console.log(foodListCorrected.length);
} else {
//don't create new food
}
process_food(i+1)
},
error: function(error) {
console.log("error");
}
});
}
// Go! Call the function with the first food.
process_food(0);
});
答案 0 :(得分:2)
我认为你对异步逻辑的问题是正确的。问题是外部循环尽可能快地完成,为您的食物查询查询启动各种较慢的异步调用。外环并不等待,因为它们知道变量提升&#39;当你访问&#39; name&#39;在你的成功功能中,它的价值将是&#39; name&#39;的最新价值。在外循环中。因此,当调用success函数时,name的值已经移动到与第一次启动exists / save查询序列时不同的食物。
这是一个非常简单的例子:
说你的foodList看起来像[&#39; Muffin&#39;],[&#39;芝士蛋糕&#39;]。当您第一次进入循环时,您的名字=&#39; Muffin&#39;。您为name =&#39; Muffin&#39;而这现在是异步发生的。同时,外环快乐地移动并设置名称=&#39;芝士蛋糕&#39;并触发另一个存在的查询。与此同时。您的第一个存在的查询完成,您现在准备保存第一个食物。但是,由于提升,你的成功函数中名称的价值现在是&#39; Cheesecake&#39;。因此它节省了&Cheesecake&#39;什么时候应该保存松饼&#39;然后第二组异步查询完成,这个也保存了&#39; Cheesecake&#39;。所以你得到两种食物,代表你们两种独特的食物,但两者都被称为“芝士蛋糕”!
这是关于变量提升的经典文章,值得一读:
http://www.adequatelygood.com/JavaScript-Scoping-and-Hoisting.html
解决这个问题的一种方法是,只有当所有异步调用当前食物完成后才触发下一个食物的处理。你可以这样做:
Parse.Cloud.define("recordFavorite", function(request, response) {
var foodList = request.params.foodList; //string array of food names
var Food = Parse.Object.extend("Food");
var query = new Parse.Query(Food);
// Wrap your logic in a function
function process_food(i) {
// Are we done?
if (i == foodList.length) return;
var name = foodList[i];
console.log("before name is " + name);
var query = new Parse.Query(Food);
query.exists("name", name);
query.find({
success: function(results) {
if(results.length == 0){
var food = new Food();
food.set("name", name);
food.save(null, {
success: function(food) {
console.log("saved with name " + name);
// Move onto the next food, only after all the async operations
// have completed.
process_food(i+1)
},
error: function(food, error) {
}
});
} else {
//don't create new food
}
},
error: function(error) {
}
});
}
// Go! Call the function with the first food.
process_food(0);
});
(注意,我没有测试过这段代码,因此可能存在语法错误。)
我之前没有遇到Parse ......我看到了你的问题,开始阅读它,并认为它看起来非常有趣!我将记住它为我的下一个PHP API项目。我认为你可以尝试做一些更聪明的事情。例如,您的方法需要每个食物有2个异步调用,一个用于查看是否存在,还有一个用于保存。对于200种食物,这是400次异步呼叫。但是,Parse API看起来非常有用,我认为它将提供工具来帮助您减少这种情况。您可以尝试以下几行:
您已经拥有要保存的名称字符串数组:
var foodList = request.params.foodList; //string array of food names
说它看起来像[&#34; Cupcakes&#34;,&#34; Muffins&#34;,&#34; Cake&#34;]。
现在构建一个Parse查询,获取服务器上已有的所有食品名称。 (我不知道该怎么做!)。但你应该回到一个阵列,让我们说[&#34; Cupcakes&#34;,&#34;芝士蛋糕&#34;]。
现在你在JavaScript中删除重复项。 StackOverflow上有一些很好的问题可以帮助解决这个问题!结果将是&#34; Cupcake&#34;是重复的,所以我们留下了数组[&#34; Muffins&#34;,&#34; Cake&#34;]
现在在Parse看起来你可以批处理一些操作:
https://parse.com/docs/rest#objects-batch
所以你的目标是通过一次API调用来保存这个[&#34; Muffins&#34;,&#34; Cake&#34;]数组。
这种方法可以很好地适应食物的数量,所以即使有200种食物,你应该能够在一次查询中完成,每50种食物更新一次(我认为50是批量限制,来自Parse docs),所以最多需要5个API调用。
答案 1 :(得分:0)
我相信这(https://www.parse.com/docs/js_guide#promises-series)是您正在寻找的解决方案。你需要利用承诺来强制同步。