Pav*_*lli 1 ajax closures asynchronous knockout.js
我从服务器抓取数据并将它们推入可观察的数组中.
我正在将observable推入一个可观察的数组中.
当我将数据推入observable时,observable包含数据.
但是,只要我将observable推入可观察数组,一些可观察数据就会丢失数据.
self.mealFoods([]);
$.ajax({
url: "/mealsurl/1",
async: false,
dataType: 'json',
success: function(datad) {
for(var lia = 0; lia < datad.length; lia++){
var cats_url = "/catsurl/" + datad[lia].category_id;
var units_by_food_url = "/unitsurl/" + datad[lia].ndb_no;
var foodThing = new NewFood();
foodThing.foodId(parseInt(datad[lia].id)); //works
foodThing.category(parseInt(datad[lia].category_id)); //works
$.ajax({
url: cats_url,
dataType: 'json',
success: function(dat) {
foodThing.category_foods(dat); //works
}
});
foodThing.food(datad[lia].ndb_no); //works
$.ajax({
url: units_by_food_url,
dataType: 'json',
success: function(dat) {
foodThing.food.units(dat); //works
}
});
foodThing.unit(parseInt(datad[lia].seq)); //works
foodThing.number_of_unit(datad[lia].this_much); //works
self.mealFoods.push(foodThing);
// At this point when looking inside the mealFoods array: self.mealFoods()[0].food(), self.mealFoods()[0].unit(), self.mealFoods()[0].food.units(), self.mealFoods()[0].category_Foods() ALL ARE EMPTY
}
}
});
Run Code Online (Sandbox Code Playgroud)
先生,你有一个经典的异步脑融化案例.这是初学者常见的sympton,但从不担心恢复率接近100%.:)
我打赌你的经验是使用同步语言,也就是说,如果一行是在另一行之后写的,那么之前写的行总是在之前执行.
普通的JavaScript函数是同步的.例如:
console.log(1);
console.log(2);
Run Code Online (Sandbox Code Playgroud)
正如所料,这打印1
然后2
.
但是,异步代码不一定按声明的顺序执行.使用setTimeout函数考虑此示例,该函数调度函数以供稍后执行:
setTimeout(function(){ console.log(1); }, 1000);
console.log(2);
Run Code Online (Sandbox Code Playgroud)
现在,输出将是2
和1
,因为1
在setTimeout
通话后仅运行1000毫秒.
所以,我想你已经开始明白这是如何适用于你的问题的.
您对通话cats_url
和units_by_food_url
是异步的.因此,以下代码不会等待它们完成.因此,当您访问时self.mealFoods()[0].food.units()
,该success
功能尚未获取数据!
您需要做的是适当地协调您的异步调用.有很多方法可以实现这一目标.首先,我将教你最简单的策略,只使用函数:
所以,它看起来像这样:
$.ajax({
url: "/meals/1",
dataType: 'json',
success: function(list) {
var observableArray = ko.observableArray([]); // this will hold your list
var length = list.length;
var tries = 0;
var listComplete = function () {
tries++;
if (tries == length) {
// Hooray!
// All your items are complete.
console.log(observableArray());
}
};
list.forEach(function(item){
var propertyOneUrl = item.propertyOneUrl;
var propertyTwoUrl = item.propertyTwoUrl;
var propertyOneComplete = false;
var propertyTwoComplete = false;
var food = new Food(item.id);
var itemComplete = function () {
if (propertyOneComplete && propertyTwoComplete) {
// This item is complete.
observableArray.push(food);
// Let's warn list complete so it can count us in.
listComplete();
}
};
// Start your ajax calls
$.ajax({
url: propertyOneUrl,
dataType: 'json',
success: function (propertyOne) {
food.propertyOne(propertyOne);
// Declare that your first property is ready
propertyOneComplete = true;
// We can't know which property finishes first, so we must call this in both
itemComplete();
}
});
$.ajax({
url: propertyTwoUrl,
dataType: 'json',
success: function (propertyTwo) {
food.propertyTwo(propertyTwo);
// Declare that your second property is ready
propertyTwoComplete = true;
// We can't know which property finishes first, so we must call this in both
itemComplete();
}
});
}); //for each
} // success
});
Run Code Online (Sandbox Code Playgroud)
现在,你可能已经意识到这种模式是多么令人烦恼.这就是为什么还有其他方法可以更好地解决这个问题.其中一种是称为"承诺"的模式.您可以在以下链接中了解有关它们的更多信息
https://www.promisejs.org/
http://blog.gadr.me/promises-are-not-optional/
你会很高兴知道jQuery.ajax()返回一个Promise!所以,现在你可以尝试使用Promises来解决这个问题.你最终会得到一个更干净的代码.
希望你成功!