推入observableArray后,可观察值消失

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)

fir*_*oit 5

先生,你有一个经典的异步脑融化案例.这是初学者常见的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)

现在,输出将是21,因为1setTimeout通话后仅运行1000毫秒.

所以,我想你已经开始明白这是如何适用于你的问题的.

您对通话cats_urlunits_by_food_url异步的.因此,以下代码不会等待它们完成.因此,当您访问时self.mealFoods()[0].food.units(),该success功能尚未获取数据!

您需要做的是适当协调您的异步调用.有很多方法可以实现这一目标.首先,我将教你最简单的策略,只使用函数:

  • 从服务器获取列表
  • 当你有列表时,迭代每顿饭并开始两个ajax调用(到目前为止,你已经做好了一切)
  • 现在到了魔术:当你对结果要么 Ajax调用,你所说的"itemComplete"功能.此功能将同步两个调用 - 只有在两个调用完成后才会继续.
  • 最后,每次完成任何项目时调用"listComplete"函数.此功能还必须在继续之前检查所有项目是否完整.

所以,它看起来像这样:

$.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来解决这个问题.你最终会得到一个更干净的代码.

希望你成功!