【问题标题】:Knock Out ObservableArray aync call binding order issueKnockout Observable Array 异步调用绑定顺序问题
【发布时间】:2017-04-25 17:41:33
【问题描述】:

我有一个 ObservableArray。使用 ajax 异步调用我正在获取数据以进行绑定。将有 1000 个数据。在每次调用中都会获取 100 条数据。问题是由于 ajax 异步调用顺序不会从 1 到 1000。由于浏览器不会响应,因此无法将其设为同步调用。如何对敲除中的异步数据进行排序?

var DataVM = ko.observableArray([]);
ko.applyBindings(DataVM, document.getElementById("ControlBlock"));

for (var i = 0; i < totalAjaxCall; i++) {
                        GetData(guid, start, end, self.DataCallback);
                        start = start + 100;
                        end = end +100;
                    }

 DataCallback= function (result) {
        var temp = JSON.parse(result.d); 
        var data = [];
        var data = temp.Data;
        for (var j = 0; j < data.length; j++) {
            var tempItem_ = new Item();
            tempItem_.Number = data[j].Number;
        // Other codes           
            DataVM.push(tempItem_ );
        }
    };

【问题讨论】:

    标签: javascript asp.net performance knockout.js


    【解决方案1】:

    您可以在收到积木时记住它们,然后在收到所有积木后重新组装。见 cmets:

    var DataVM = ko.observableArray([]);
    ko.applyBindings(DataVM, document.getElementById("ControlBlock"));
    
    // Remember the results in a temporary array of arrays
    var received = 0;
    var receivedBlocks = [];
    
    for (var i = 0; i < totalAjaxCall; i++) {
        // Tell `DataCallback` which block it's going to get
        GetData(guid, start, end, self.DataCallback.bind(null, i));
        // No need for `self` ----^^^^^
        start = start + 100;
        end = end +100;
    }
    
    DataCallback = function (i, result) {
        // Create and remember the items for this block
        receivedBlocks[i] = JSON.parse(result.d).map(function(e) {
            var tempItem_ = new Item();
            tempItem_.Number = num;
            return tempItem_;
        });
        ++received;
    
        // Do we have them all?
        if (received == totalAjaxCall) {
            // Flatten our array of arrays, now we have all the pieces
            var receivedItems = []
            receivedBlocks.forEach(function(block) {
                receivedItems.push.apply(result, block);
            });
    
            // Push all of those onto DataVM as a single operation
            // Note: You were using `DataVM.push`, so I used that here,
            // but if you wanted to *replace* the contents of `DataVM`,
            // (or if you know it's empty), you'd just do:
            //      DataVM(receivedItems);
            // instead.
            DataVM.push.apply(DataVM, receivedItems);
        }
    };
    

    【讨论】:

    • @排序将如何进行?
    • 它是通过我们构建receivedBlocks的方式处理的:注意我们传递了DataCallback块的索引(i),然后使用它来填充receivedBlocks:@ 987654326@。因此,如果块 3 在块 2 之前进入并不重要,我们将其存储在 receivedBlocks 的正确位置。 (数组在 JavaScript 中可能有漏洞,它们本质上是 sparse。)所以当我们知道我们拥有所有块时,我们知道 receivedBlocks 中的所有插槽都已被填充,在订购。
    • 这样会同时加载全部1000条数据?
    • @James:它在最后一次填充可观察数组,是的,当所有片段都被检索到时。如果需要,我们可以逐步填写。
    • @James:嗯,你没这么说吧? :-) 但是如果您愿意,可以显示部分结果,只需稍微调整代码即可。所有的碎片都在那里。
    【解决方案2】:

    我不确定你想偏离你当前的代码多远,但我想宣传一些淘汰赛的附加功能:)

    如果您为请求创建一个小的“中间”模型,您可以使用computed 值来自动跟踪正确排序的数据列表。

    例如,如果您像这样定义new Request()

    var Request = function(start, end) {
      this.completed = ko.observable(false);
      this.data = [];
    
      getData(start, end, this.onLoad.bind(this));
    };
    
    Request.prototype.onLoad = function(data) {
      this.data = data;
      this.completed(true);
    };
    

    您可以更改 for 循环来创建那些“中间”模型。这将为0100101201 等创建一个Request。这些模型中的每一个都按创建顺序存储在一个数组中。

    function getDataRequests(start, end, chunkSize) {
      var requests = [];
      for (var i = start; i < end; i += chunkSize) {
        requests.push(new Request(i, Math.min(i + chunkSize, end)));
      }
      return requests;
    };
    

    现在您可以创建一个有序数组,您可以通过将所有completed 请求合并在一起来计算另一个data 的有序数组:

    var DataVM = function(start, end, chunkSize) {
      // We keep track of a list of requests
      var requests = ko.observableArray(
        getDataRequests(start, end, chunkSize)
      );
    
      // Because requests have an observable completed prop,
      // we can automatically keep track of a list of completed
      // requests
      var completedRequests = ko.pureComputed(() =>
        requests().filter(r => r.completed()));
    
      // Now, whenever a requests completes, we flatten the 
      // `data` parts for `completed` requests
      this.data = ko.pureComputed(() => completedRequests()
        .reduce((items, r) => items.concat(r.data), []));
    };
    

    因为您拥有requests 数组,所以您可以轻松计算 UI 属性。例如:firstLoaded 是一个计算值,它返回您的 first 请求的 completed 值。

    这是一个完整的例子(ES2015):

    var DataVM = function(start, end, chunkSize) {
      // We keep track of a list of requests
      var requests = ko.observableArray(
        getDataRequests(start, end, chunkSize)
      );
    
      // Because requests have an observable completed prop,
      // we can automatically keep track of a list of completed
      // requests
      var completedRequests = ko.pureComputed(() =>
                                              requests().filter(r => r.completed()));
    
      // Now, whenever a requests completes, we flatten the 
      // `data` parts for `completed` requests
      this.data = ko.pureComputed(() => completedRequests()
                                  .reduce((items, r) => items.concat(r.data), []));
    
      // Shows progress
      this.loadingMsg = ko.pureComputed(() => {
        var completedCount = completedRequests().length,
          	allCount = requests().length;
    
        return completedCount === allCount 
        	? `Done loading ${end - start} items in ${allCount} steps` 
          : `Loading... (${completedCount}/${allCount})`;
      });
    
      // Check if the first (if any) request has completed loading
      this.firstCompleted = ko.pureComputed(() =>
        requests().length && requests()[0].completed());
    };
    
    
    var Request = function(start, end) {
      this.completed = ko.observable(false);
      this.data = [];
    
      getData(start, end, this.onLoad.bind(this));
    };
    
    Request.prototype.onLoad = function(data) {
      this.data = data;
      this.completed(true);
    };
    
    var vm = new DataVM(0, 50, 5);
    ko.applyBindings(vm);
    
    // Mock async ajax stuff and data getters
    function getDataRequests(start, end, chunkSize) {
      var requests = [];
      for (var i = start; i < end; i += chunkSize) {
        requests.push(new Request(i, Math.min(i + chunkSize, end)));
      }
      return requests;
    };
    
    function getData(start, end, cb) {
      setTimeout(function() {
      	cb(mockData(start, end));
      }, Math.random() * 3000 + 500);
    }
    
    function mockData(from, to) {
    	return Array(to - from).fill(from).map(function(_, i) {
          return from + i;
      });
    }
    <script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.2.0/knockout-min.js"></script>
    <div data-bind="text: loadingMsg"></div>
    
    <ul data-bind="foreach: data, visible: firstCompleted" style="border: 1px solid black;">
      <li data-bind="text: $data"></li>
    </ul>

    【讨论】:

      猜你喜欢
      • 2014-11-27
      • 2017-12-13
      • 2012-06-14
      • 2015-04-05
      • 2015-08-15
      • 2013-04-22
      • 1970-01-01
      • 2012-12-11
      • 1970-01-01
      相关资源
      最近更新 更多