Javascript Promises - 在for循环之前保存项目

时间:2014-09-13 21:23:51

标签: javascript xml-parsing parse-platform cloud promise

我有一个解析云代码函数,在这个函数中我对一些项目执行查询然后使用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);
});
});

1 个答案:

答案 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);
    });
});

未经测试,因此可能需要调试