我有一个解析云代码函数,在这个函数中我对一些项目执行查询然后使用for循环我保存了一些这些项目。但是for循环继续并且没有正确保存一些项目。
这是代码的一般版本:
Parse.Cloud.define("saveCurrentDayItems", function(request, response) {
var xmlReader = require('cloud/xmlreader.js');
var MenuURL = Parse.Object.extend("MenuURL");
var queryMenuURL = new Parse.Query(MenuURL);
queryMenuURL.find().then(function(resultsMenuURL) {
//********************************************************************************************************
//I want the save to happen before it goes thought this for loop for the second time, and so on
for (var i = 0; i < resultsMenuURL.length; i++) {
var location = resultsMenuURL[i].get("Location");
Parse.Cloud.httpRequest({
url: url,
success: function(httpResponse) {
var xmlData = httpResponse.text;
xmlReader.read(xmlData, function (err, res){
if(err) return console.log(err);
for (var i3 = 0; i3 < res.menu.day.at(dayNumber).meal.count(); i3++) {
var meal = res.menu.day.at(dayNumber).meal.at(i3).attributes().name;
testItem.set("meal", meal);
testItem.set("location", location);
testItem.save().then(function(testItem) {
});
}
}
});
},
error: function(httpResponse) {
console.error('Request failed with response code ' + httpResponse.status);
}
});
}
});
});
我查看了解析文档,但我无法理解它们,我无法理解的承诺部分。
非常感谢你提前帮助
编辑2
当我有这样的代码时,我收到错误TypeError: Cannot call method 'reduce' of undefined
Parse.Cloud.define("saveCurrentDayItems23", function(request, response) {
var xmlReader = require('cloud/xmlreader.js');
//First worker function, promisify xmlReader.read();
function readResponse_async(xlmString) {
var promise = new Parse.Promise();
xmlReader.read(xlmString, function (err, res) {
if(err) {
promise.reject(err);
} else {
promise.resolve(res);
results = res;
}
});
return promise;
}
//Second worker function, perform a series of saves
function saveMeals_async(meals, location, testItem) {
return meals.reduce(function(promise, meal) {
return promise.then(function() {
testItem.set("meal", meal.attributes().name);
//the line above does not work it cannot get meal, it is undefined
testItem.set("location", location);
return testItem.save();
});
}, Parse.Promise.as());
}
var MenuURL = Parse.Object.extend("MenuURL");
var queryMenuURL = new Parse.Query(MenuURL);
//Master routine
queryMenuURL.find().then(function(resultsMenuURL) {
for (var i = 0; i < resultsMenuURL.length; i++) {
var url = resultsMenuURL[i].get('URL');
return resultsMenuURL.reduce(function(promise, item) {
return promise.then(function() {
return Parse.Cloud.httpRequest({
url: url,
//data: ... //some properties of item?
}).then(function(httpResponse) {
return readResponse_async(httpResponse.text).then(function() {
var TestItem = Parse.Object.extend("TestItem");
var testItem = new TestItem();
return saveMeals_async(result.menu.day.meal.counter.dish.name.text(),item.get("Location"),
testItem);
//this line above does not work, it sends only one string, not an array, so reduce cannot be called
});
});
});
}, Parse.Promise.as());
}
}).fail(function(err) {
console.error(err);
});
});
答案 0 :(得分:1)
要问这个问题(&#34;我希望保存在第二次进入这个for循环之前发生,依此类推&#34;),相当复杂 - 不是真正的初学者& #39;问题。
这里似乎有几个异步操作,即:
queryMenuURL.find()
Parse.Cloud.httpRequest()
xmlReader.read()
testItem.save()
这些操作需要彼此合作才能产生预期效果。
queryMenuURL.find()
,Parse.Cloud.httpRequest()
和testItem.save()
每个似乎都会返回一个承诺,而xmlReader.read()
会采用节点样式回调,这会使事情略显尴尬,但也不会太糟糕。< / p>
您可以将代码编写为一个大块,但最终会在模式中使用模式。为了使所有内容都可读,您可以将一些代码作为(readabe)工作函数提取出来,留下一个(可读的)主例程。
要将当前的外部for
循环转换为一组顺序操作,您需要使用以下模式,该模式利用Array.prototype.reduce()
构建.then
链,并返回一个承诺:
function doThings_async(arr) {
return arr.reduce(function(promise, item) {
return promise.then(function(result) {
return doSomething_async(item, result);
});
}, resolvedPromise);
}
您将在下面看到此模式也用于内部for
循环,但存在其他可能性。
Parse.Cloud.define("saveCurrentDayItems", function(request, response) {
var xmlReader = require('cloud/xmlreader.js');
//First worker function, promisify xmlReader.read();
function readResponse_async(xlmString) {
var promise = new Parse.Promise();
xmlReader.read(xlmString, function (err, res) {
if(err) {
promise.reject(err);
} else {
promise.resolve(res);
}
}
return promise;
}
//Second worker function, perform a series of saves
function saveMeals_async(meals, location, testItem) {
return meals.reduce(function(promise, meal) {
return promise.then(function() {
testItem.set("meal", meal.attributes().name);
testItem.set("location", location);
return testItem.save();
});
}, Parse.Promise.as());
}
var MenuURL = Parse.Object.extend("MenuURL");
var queryMenuURL = new Parse.Query(MenuURL);
//Master routine
queryMenuURL.find().then(function(resultsMenuURL) {
...
return resultsMenuURL.reduce(function(promise, item) {
return promise.then(function() {
return Parse.Cloud.httpRequest({
url: url,
//data: ... //some properties of item?
}).then(function(httpResponse) {
return readResponse_async(httpResponse).then(function() {
return saveMeals_async(res.menu.day.at(dayNumber).meal, item.get("Location"), testItem);
});
});
});
}, Parse.Promise.as());
}).fail(function(err) {
console.error(err);
});
});
saveMeals_async()
可以编写以并行而非串行执行其保存,但这取决于您的需求。对于并行保存,只需要使用不同的模式重写saveMeals_async()
。
根据问题中的修改修改代码。
由于saveMeals_async()
中的更改,arr.reduce(...)
模式现在仅在主例程中使用一次。
Parse.Cloud.define("saveCurrentDayItems", function(request, response) {
// ***
// insert all the Date/dayNumber code here
// ***
var xmlReader = require('cloud/xmlreader.js');
//First worker function, promisify xmlReader.read();
function readResponse_async(xlmString) {
var promise = new Parse.Promise();
xmlReader.read(xlmString, function (err, res) {
if(err) {
promise.reject(err);
} else {
promise.resolve(res);
}
}
return promise;
}
//Second worker function, perform a series of saves
function saveMeals_async(meals, school, diningHallNumber, menuLocation) {
//Declare all vars up front
var i3, i4, i5, m,
TestItem = Parse.Object.extend("TestItem"),//can be reused within the loops?
promise = Parse.Promise.as();//resolved promise to start a long .then() chain
for (i3 = 0; i3 < meals.count(); i3++) {
m = meals.at(i3);
//get number of stations in day
for (i4 = 0; i4 < m.counter.count(); i4++) {
//get number of items at each station
for (i5 = 0; i5 < m.counter.at(i4).dish.count(); i5++) {
//Here a self-executing function is used to form a closure trapping `testItem`.
//Otherwise the `testItem` used in `promise.then(...)` would be the last
//remaining `testItem` created when all iterations are complete.
(function(testItem) {
testItem.set("item", m.counter.at(i4).dish.at(i5).name.text());
testItem.set("meal", m.attributes().name);
testItem.set("school", school);
testItem.set("diningHallNumber", diningHallNumber);
testItem.set("schoolMenu", menuLocation);
//build the .then() chain
promise = promise.then(function() {
return testItem.save();
});
})(new TestItem());
});
}
}
return promise;
}
var MenuURL = Parse.Object.extend("MenuURL");
var queryMenuURL = new Parse.Query(MenuURL);
//Master routine
queryMenuURL.find().then(function(resultsMenuURL) {
return resultsMenuURL.reduce(function(promise, menuItem) {
var url = menuItem.get('URL'),
school = menuItem.get("school"),
diningHallNumber = menuItem.get("diningHallNumber"),
menuLocation = menuItem.get("menuLocation");
return promise.then(function() {
return Parse.Cloud.httpRequest({
url: url,
//data: ... //some properties of menuItem?
}).then(function(httpResponse) {
return readResponse_async(httpResponse).then(function(res) {
if (res.menu.day.at(dayNumber).meal) {
return saveMeals_async(res.menu.day.at(dayNumber).meal, school, diningHallNumber, menuLocation);
} else {
return Parse.Promise.as();//return resolved promise to keep the promise chain going.
}
});
});
});
}, Parse.Promise.as());
}).fail(function(err) {
console.error(err);
});
});
未经测试,因此可能需要调试