【问题标题】:Javascript Promises - save item before for loopJavascript Promises - 在 for 循环之前保存项目
【发布时间】:2014-09-13 21:23:50
【问题描述】:

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

【问题讨论】:

  • 如果您需要帮助,您需要更具体地了解问题到底是什么以及您需要帮助的具体内容。请参考您的代码的特定部分,描述您希望它做什么,并解释当前发生的与此不同的事情。例如,您说您希望在第一个 for 循环之前进行保存,但似乎与保存有关的唯一代码不仅深埋在 for 循环中,而且还嵌入了另一个在里面。不清楚你想做什么。
  • @jfriend00 好的,尽量做到具体。我有这个返回几个 URL 的查询,然后从这些 url 中获取一个 http 请求,以便我可以解析一些数据。一旦我解析了其中的一些数据,我想保存一些原始数据和一些解析后的数据。我的目标是在 for 循环再次运行之前保存数据?这有意义吗?

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


【解决方案1】:

按照问题的要求去做(“我希望保存在它第二次[通过]这个 for 循环之前发生,依此类推”),这是相当复杂的 - 并不是真正的初学者问题。

您在这里似乎有几个异步操作,即:

  • queryMenuURL.find()
  • Parse.Cloud.httpRequest()
  • xmlReader.read()
  • testItem.save()

这些操作需要相互配合才能达到预期的效果。

queryMenuURL.find()Parse.Cloud.httpRequest()testItem.save() 似乎都返回了一个 Promise,而 xmlReader.read() 采用了节点样式的回调,这让事情有点尴尬但还不错。

你可以把代码写成一个大块,但你最终会得到模式中的模式。为了使所有内容都可读,您可以将一些代码作为(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);
    });
});

未经测试,可能需要调试

【讨论】:

  • 我在遵循代码时遇到了一些问题,那是什么“...”,你可以添加更多的 cmets,我已经添加了上面原始代码的完整版本,因为我有多个xml 字符串。这个异步的函数在哪里?
  • 我开始越来越了解代码,但我在第 44 行遇到错误,说 res 未定义
  • 是的,我犯了一个错误,会给出res 错误。把它留给我,我会为你发布一些修改过的代码,考虑到你的编辑。
  • 好吧,它非常接近,但我收到此错误:Error: TypeError: Object [object Object] has no method 'forEach' 我认为这是因为m.counter.at(i4).dish 这一行没有返回数组
  • 这里是一个xml的例子,如果它有帮助64.182.231.116/~spencerf/university_of_albany/…
猜你喜欢
  • 2013-04-12
  • 1970-01-01
  • 1970-01-01
  • 2020-10-04
  • 1970-01-01
  • 2021-09-30
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多